diff --git a/harness/temporalHelpers.js b/harness/temporalHelpers.js
index dfbffd776ee..a3579b0b782 100644
--- a/harness/temporalHelpers.js
+++ b/harness/temporalHelpers.js
@@ -71,17 +71,18 @@ var TemporalHelpers = {
},
/*
- * assertPlainMonthDay(monthDay, monthCode, day[, description]):
+ * assertPlainMonthDay(monthDay, monthCode, day[, description [, referenceISOYear]]):
*
* Shorthand for asserting that each field of a Temporal.PlainMonthDay is
* equal to an expected value. (Except the `calendar` property, since callers
* may want to assert either object equality with an object they put in there,
* or the result of monthDay.calendar.toString().)
*/
- assertPlainMonthDay(monthDay, monthCode, day, description = "") {
+ assertPlainMonthDay(monthDay, monthCode, day, description = "", referenceISOYear = 1972) {
assert(monthDay instanceof Temporal.PlainMonthDay, `${description} instanceof`);
assert.sameValue(monthDay.monthCode, monthCode, `${description} monthCode result`);
assert.sameValue(monthDay.day, day, `${description} day result`);
+ assert.sameValue(monthDay.getISOFields().isoYear, referenceISOYear, `${description} referenceISOYear result`);
},
/*
@@ -116,6 +117,662 @@ var TemporalHelpers = {
assert.sameValue(yearMonth.month, month, `${description} month result`);
assert.sameValue(yearMonth.monthCode, monthCode, `${description} monthCode result`);
},
+
+ /*
+ * assertUnreachable(description):
+ *
+ * Helper for asserting that code is not executed. This is useful for
+ * assertions that methods of user calendars and time zones are not called.
+ */
+ assertUnreachable(description) {
+ let message = "This code should not be executed";
+ if (description) {
+ message = `${message}: ${description}`;
+ }
+ throw new Test262Error(message);
+ },
+
+ /*
+ * checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls):
+ *
+ * When an options object with a largestUnit property is synthesized inside
+ * Temporal and passed to user code such as calendar.dateUntil(), the value of
+ * the largestUnit property should be in the singular form, even if the input
+ * was given in the plural form.
+ * (This doesn't apply when the options object is passed through verbatim.)
+ *
+ * func(calendar, largestUnit, index) is the operation under test. It's called
+ * with an instance of a calendar that keeps track of which largestUnit is
+ * passed to dateUntil(), each key of expectedLargestUnitCalls in turn, and
+ * the key's numerical index in case the function needs to generate test data
+ * based on the index. At the end, the actual values passed to dateUntil() are
+ * compared with the array values of expectedLargestUnitCalls.
+ */
+ checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls) {
+ const actual = [];
+
+ class DateUntilOptionsCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateUntil(earlier, later, options) {
+ actual.push(options.largestUnit);
+ return super.dateUntil(earlier, later, options);
+ }
+
+ toString() {
+ return "date-until-options";
+ }
+ }
+
+ const calendar = new DateUntilOptionsCalendar();
+ Object.entries(expectedLargestUnitCalls).forEach(([largestUnit, expected], index) => {
+ func(calendar, largestUnit, index);
+ assert.compareArray(actual, expected, `largestUnit passed to calendar.dateUntil() for largestUnit ${largestUnit}`);
+ actual.splice(0, actual.length); // empty it for the next check
+ });
+ },
+
+ /*
+ * checkFractionalSecondDigitsOptionWrongType(temporalObject):
+ *
+ * Checks the string-or-number type handling of the fractionalSecondDigits
+ * option to the various types' toString() methods. temporalObject is an
+ * instance of the Temporal type under test.
+ */
+ checkFractionalSecondDigitsOptionWrongType(temporalObject) {
+ // null is not a number, and converts to the string "null", which is an invalid string value
+ assert.throws(RangeError, () => temporalObject.toString({ fractionalSecondDigits: null }), "null");
+ // Booleans are not numbers, and convert to the strings "true" or "false", which are invalid
+ assert.throws(RangeError, () => temporalObject.toString({ fractionalSecondDigits: true }), "true");
+ assert.throws(RangeError, () => temporalObject.toString({ fractionalSecondDigits: false }), "false");
+ // Symbols are not numbers and cannot convert to strings
+ assert.throws(TypeError, () => temporalObject.toString({ fractionalSecondDigits: Symbol() }), "symbol");
+ // BigInts are not numbers and convert to strings which are invalid
+ assert.throws(RangeError, () => temporalObject.toString({ fractionalSecondDigits: 2n }), "bigint");
+
+ // Objects are not numbers and prefer their toString() methods when converting to a string
+ assert.throws(RangeError, () => temporalObject.toString({ fractionalSecondDigits: {} }), "plain object");
+
+ const toStringExpected = temporalObject.toString({ fractionalSecondDigits: 'auto' });
+ const expected = [
+ "get fractionalSecondDigits.toString",
+ "call fractionalSecondDigits.toString",
+ ];
+ const actual = [];
+ const observer = TemporalHelpers.toPrimitiveObserver(actual, "auto", "fractionalSecondDigits");
+ const result = temporalObject.toString({ fractionalSecondDigits: observer });
+ assert.sameValue(result, toStringExpected, "object with toString");
+ assert.compareArray(actual, expected, "order of operations");
+ },
+
+ /*
+ * checkPlainDateTimeConversionFastPath(func):
+ *
+ * ToTemporalDate and ToTemporalTime should both, if given a
+ * Temporal.PlainDateTime instance, convert to the desired type by reading the
+ * PlainDateTime's internal slots, rather than calling any getters.
+ *
+ * func(datetime, calendar) is the actual operation to test, that must
+ * internally call the abstract operation ToTemporalDate or ToTemporalTime.
+ * It is passed a Temporal.PlainDateTime instance, as well as the instance's
+ * calendar object (so that it doesn't have to call the calendar getter itself
+ * if it wants to make any assertions about the calendar.)
+ */
+ checkPlainDateTimeConversionFastPath(func) {
+ const actual = [];
+ const expected = [];
+
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+ const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDateTime.prototype);
+ ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => {
+ Object.defineProperty(datetime, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${property}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${property}`);
+ return value;
+ },
+ };
+ },
+ });
+ });
+ Object.defineProperty(datetime, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ func(datetime, calendar);
+ assert.compareArray(actual, expected, "property getters not called");
+ },
+
+ /*
+ * Check that an options bag that accepts units written in the singular form,
+ * also accepts the same units written in the plural form.
+ * func(unit) should call the method with the appropriate options bag
+ * containing unit as a value. This will be called twice for each element of
+ * validSingularUnits, once with singular and once with plural, and the
+ * results of each pair should be the same (whether a Temporal object or a
+ * primitive value.)
+ */
+ checkPluralUnitsAccepted(func, validSingularUnits) {
+ const plurals = {
+ year: 'years',
+ month: 'months',
+ week: 'weeks',
+ day: 'days',
+ hour: 'hours',
+ minute: 'minutes',
+ second: 'seconds',
+ millisecond: 'milliseconds',
+ microsecond: 'microseconds',
+ nanosecond: 'nanoseconds',
+ };
+
+ validSingularUnits.forEach((unit) => {
+ const singularValue = func(unit);
+ const pluralValue = func(plurals[unit]);
+ if (singularValue instanceof Temporal.Duration) {
+ assert.sameValue(pluralValue.years, singularValue.years, "years value");
+ assert.sameValue(pluralValue.months, singularValue.months, "months value");
+ assert.sameValue(pluralValue.weeks, singularValue.weeks, "weeks value");
+ assert.sameValue(pluralValue.days, singularValue.days, "days value");
+ assert.sameValue(pluralValue.hours, singularValue.hours, "hours value");
+ assert.sameValue(pluralValue.minutes, singularValue.minutes, "minutes value");
+ assert.sameValue(pluralValue.seconds, singularValue.seconds, "seconds value");
+ assert.sameValue(pluralValue.milliseconds, singularValue.milliseconds, "milliseconds value");
+ assert.sameValue(pluralValue.microseconds, singularValue.microseconds, "microseconds value");
+ assert.sameValue(pluralValue.nanoseconds, singularValue.nanoseconds, "nanoseconds value");
+ } else if (
+ singularValue instanceof Temporal.Instant ||
+ singularValue instanceof Temporal.PlainDateTime ||
+ singularValue instanceof Temporal.PlainTime ||
+ singularValue instanceof Temporal.ZonedDateTime
+ ) {
+ assert(pluralValue.equals(singularValue), "Temporal objects equal");
+ } else {
+ assert.sameValue(pluralValue, singularValue);
+ }
+ });
+ },
+
+ /*
+ * checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc):
+ *
+ * Checks the type handling of the roundingIncrement option.
+ * checkFunc(roundingIncrement) is a function which takes the value of
+ * roundingIncrement to test, and calls the method under test with it,
+ * returning the result. assertTrueResultFunc(result, description) should
+ * assert that result is the expected result with roundingIncrement: true, and
+ * assertObjectResultFunc(result, description) should assert that result is
+ * the expected result with roundingIncrement being an object with a valueOf()
+ * method.
+ */
+ checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc) {
+ // null converts to 0, which is out of range
+ assert.throws(RangeError, () => checkFunc(null), "null");
+ // Booleans convert to either 0 or 1, and 1 is allowed
+ const trueResult = checkFunc(true);
+ assertTrueResultFunc(trueResult, "true");
+ assert.throws(RangeError, () => checkFunc(false), "false");
+ // Symbols and BigInts cannot convert to numbers
+ assert.throws(TypeError, () => checkFunc(Symbol()), "symbol");
+ assert.throws(TypeError, () => checkFunc(2n), "bigint");
+
+ // Objects prefer their valueOf() methods when converting to a number
+ assert.throws(RangeError, () => checkFunc({}), "plain object");
+
+ const expected = [
+ "get roundingIncrement.valueOf",
+ "call roundingIncrement.valueOf",
+ ];
+ const actual = [];
+ const observer = TemporalHelpers.toPrimitiveObserver(actual, 2, "roundingIncrement");
+ const objectResult = checkFunc(observer);
+ assertObjectResultFunc(objectResult, "object with valueOf");
+ assert.compareArray(actual, expected, "order of operations");
+ },
+
+ /*
+ * checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc):
+ *
+ * Checks the type handling of a string option, of which there are several in
+ * Temporal.
+ * propertyName is the name of the option, and value is the value that
+ * assertFunc should expect it to have.
+ * checkFunc(value) is a function which takes the value of the option to test,
+ * and calls the method under test with it, returning the result.
+ * assertFunc(result, description) should assert that result is the expected
+ * result with the option value being an object with a toString() method
+ * which returns the given value.
+ */
+ checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc) {
+ // null converts to the string "null", which is an invalid string value
+ assert.throws(RangeError, () => checkFunc(null), "null");
+ // Booleans convert to the strings "true" or "false", which are invalid
+ assert.throws(RangeError, () => checkFunc(true), "true");
+ assert.throws(RangeError, () => checkFunc(false), "false");
+ // Symbols cannot convert to strings
+ assert.throws(TypeError, () => checkFunc(Symbol()), "symbol");
+ // Numbers convert to strings which are invalid
+ assert.throws(RangeError, () => checkFunc(2), "number");
+ // BigInts convert to strings which are invalid
+ assert.throws(RangeError, () => checkFunc(2n), "bigint");
+
+ // Objects prefer their toString() methods when converting to a string
+ assert.throws(RangeError, () => checkFunc({}), "plain object");
+
+ const expected = [
+ `get ${propertyName}.toString`,
+ `call ${propertyName}.toString`,
+ ];
+ const actual = [];
+ const observer = TemporalHelpers.toPrimitiveObserver(actual, value, propertyName);
+ const result = checkFunc(observer);
+ assertFunc(result, "object with toString");
+ assert.compareArray(actual, expected, "order of operations");
+ },
+
+ /*
+ * checkSubclassingIgnored(construct, constructArgs, method, methodArgs,
+ * resultAssertions):
+ *
+ * Methods of Temporal classes that return a new instance of the same class,
+ * must not take the constructor of a subclass into account, nor the @@species
+ * property. This helper runs tests to ensure this.
+ *
+ * construct(...constructArgs) must yield a valid instance of the Temporal
+ * class. instance[method](...methodArgs) is the method call under test, which
+ * must also yield a valid instance of the same Temporal class, not a
+ * subclass. See below for the individual tests that this runs.
+ * resultAssertions() is a function that performs additional assertions on the
+ * instance returned by the method under test.
+ */
+ checkSubclassingIgnored(...args) {
+ this.checkSubclassConstructorNotObject(...args);
+ this.checkSubclassConstructorUndefined(...args);
+ this.checkSubclassConstructorThrows(...args);
+ this.checkSubclassConstructorNotCalled(...args);
+ this.checkSubclassSpeciesInvalidResult(...args);
+ this.checkSubclassSpeciesNotAConstructor(...args);
+ this.checkSubclassSpeciesNull(...args);
+ this.checkSubclassSpeciesUndefined(...args);
+ this.checkSubclassSpeciesThrows(...args);
+ },
+
+ /*
+ * Checks that replacing the 'constructor' property of the instance with
+ * various primitive values does not affect the returned new instance.
+ */
+ checkSubclassConstructorNotObject(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const instance = new construct(...constructArgs);
+ instance.constructor = value;
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
+ resultAssertions(result);
+ }
+
+ check(null, "null");
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "Symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ },
+
+ /*
+ * Checks that replacing the 'constructor' property of the subclass with
+ * undefined does not affect the returned new instance.
+ */
+ checkSubclassConstructorUndefined(construct, constructArgs, method, methodArgs, resultAssertions) {
+ let called = 0;
+
+ class MySubclass extends construct {
+ constructor() {
+ ++called;
+ super(...constructArgs);
+ }
+ }
+
+ const instance = new MySubclass();
+ assert.sameValue(called, 1);
+
+ MySubclass.prototype.constructor = undefined;
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(called, 1);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Checks that making the 'constructor' property of the instance throw when
+ * called does not affect the returned new instance.
+ */
+ checkSubclassConstructorThrows(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function CustomError() {}
+ const instance = new construct(...constructArgs);
+ Object.defineProperty(instance, "constructor", {
+ get() {
+ throw new CustomError();
+ }
+ });
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Checks that when subclassing, the subclass constructor is not called by
+ * the method under test.
+ */
+ checkSubclassConstructorNotCalled(construct, constructArgs, method, methodArgs, resultAssertions) {
+ let called = 0;
+
+ class MySubclass extends construct {
+ constructor() {
+ ++called;
+ super(...constructArgs);
+ }
+ }
+
+ const instance = new MySubclass();
+ assert.sameValue(called, 1);
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(called, 1);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it's a
+ * constructor that returns a non-object value.
+ */
+ checkSubclassSpeciesInvalidResult(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const instance = new construct(...constructArgs);
+ instance.constructor = {
+ [Symbol.species]: function() {
+ return value;
+ },
+ };
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
+ resultAssertions(result);
+ }
+
+ check(undefined, "undefined");
+ check(null, "null");
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "Symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ check({}, "plain object");
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it's not a
+ * constructor.
+ */
+ checkSubclassSpeciesNotAConstructor(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const instance = new construct(...constructArgs);
+ instance.constructor = {
+ [Symbol.species]: value,
+ };
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
+ resultAssertions(result);
+ }
+
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "Symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ check({}, "plain object");
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it's null.
+ */
+ checkSubclassSpeciesNull(construct, constructArgs, method, methodArgs, resultAssertions) {
+ let called = 0;
+
+ class MySubclass extends construct {
+ constructor() {
+ ++called;
+ super(...constructArgs);
+ }
+ }
+
+ const instance = new MySubclass();
+ assert.sameValue(called, 1);
+
+ MySubclass.prototype.constructor = {
+ [Symbol.species]: null,
+ };
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(called, 1);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it's
+ * undefined.
+ */
+ checkSubclassSpeciesUndefined(construct, constructArgs, method, methodArgs, resultAssertions) {
+ let called = 0;
+
+ class MySubclass extends construct {
+ constructor() {
+ ++called;
+ super(...constructArgs);
+ }
+ }
+
+ const instance = new MySubclass();
+ assert.sameValue(called, 1);
+
+ MySubclass.prototype.constructor = {
+ [Symbol.species]: undefined,
+ };
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(called, 1);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it throws,
+ * i.e. it is not called at all.
+ */
+ checkSubclassSpeciesThrows(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function CustomError() {}
+
+ const instance = new construct(...constructArgs);
+ instance.constructor = {
+ get [Symbol.species]() {
+ throw new CustomError();
+ },
+ };
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ },
+
+ /*
+ * checkSubclassingIgnoredStatic(construct, method, methodArgs, resultAssertions):
+ *
+ * Static methods of Temporal classes that return a new instance of the class,
+ * must not use the this-value as a constructor. This helper runs tests to
+ * ensure this.
+ *
+ * construct[method](...methodArgs) is the static method call under test, and
+ * must yield a valid instance of the Temporal class, not a subclass. See
+ * below for the individual tests that this runs.
+ * resultAssertions() is a function that performs additional assertions on the
+ * instance returned by the method under test.
+ */
+ checkSubclassingIgnoredStatic(...args) {
+ this.checkStaticInvalidReceiver(...args);
+ this.checkStaticReceiverNotCalled(...args);
+ this.checkThisValueNotCalled(...args);
+ },
+
+ /*
+ * Check that calling the static method with a receiver that's not callable,
+ * still calls the intrinsic constructor.
+ */
+ checkStaticInvalidReceiver(construct, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const result = construct[method].apply(value, methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ }
+
+ check(undefined, "undefined");
+ check(null, "null");
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ check({}, "Non-callable object");
+ },
+
+ /*
+ * Check that calling the static method with a receiver that returns a value
+ * that's not callable, still calls the intrinsic constructor.
+ */
+ checkStaticReceiverNotCalled(construct, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const receiver = function () {
+ return value;
+ };
+ const result = construct[method].apply(receiver, methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ }
+
+ check(undefined, "undefined");
+ check(null, "null");
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ check({}, "Non-callable object");
+ },
+
+ /*
+ * Check that the receiver isn't called.
+ */
+ checkThisValueNotCalled(construct, method, methodArgs, resultAssertions) {
+ let called = false;
+
+ class MySubclass extends construct {
+ constructor(...args) {
+ called = true;
+ super(...args);
+ }
+ }
+
+ const result = MySubclass[method](...methodArgs);
+ assert.sameValue(called, false);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Check that any iterable returned from a custom time zone's
+ * getPossibleInstantsFor() method is exhausted.
+ * The custom time zone object is passed in to func().
+ * expected is an array of strings representing the expected calls to the
+ * getPossibleInstantsFor() method. The PlainDateTimes that it is called with,
+ * are compared (using their toString() results) with the array.
+ */
+ checkTimeZonePossibleInstantsIterable(func, expected) {
+ // A custom time zone that returns an iterable instead of an array from its
+ // getPossibleInstantsFor() method, and for testing purposes skips
+ // 00:00-01:00 UTC on January 1, 2030, and repeats 00:00-01:00 UTC+1 on
+ // January 3, 2030. Otherwise identical to the UTC time zone.
+ class TimeZonePossibleInstantsIterable extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.getPossibleInstantsForCallCount = 0;
+ this.getPossibleInstantsForCalledWith = [];
+ this.getPossibleInstantsForReturns = [];
+ this.iteratorExhausted = [];
+ }
+
+ toString() {
+ return "Custom/Iterable";
+ }
+
+ getOffsetNanosecondsFor(instant) {
+ if (Temporal.Instant.compare(instant, "2030-01-01T00:00Z") >= 0 &&
+ Temporal.Instant.compare(instant, "2030-01-03T01:00Z") < 0) {
+ return 3600_000_000_000;
+ } else {
+ return 0;
+ }
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ this.getPossibleInstantsForCallCount++;
+ this.getPossibleInstantsForCalledWith.push(dateTime);
+
+ // Fake DST transition
+ let retval = super.getPossibleInstantsFor(dateTime);
+ if (dateTime.toPlainDate().equals("2030-01-01") && dateTime.hour === 0) {
+ retval = [];
+ } else if (dateTime.toPlainDate().equals("2030-01-03") && dateTime.hour === 0) {
+ retval.push(retval[0].subtract({ hours: 1 }));
+ } else if (dateTime.year === 2030 && dateTime.month === 1 && dateTime.day >= 1 && dateTime.day <= 2) {
+ retval[0] = retval[0].subtract({ hours: 1 });
+ }
+
+ this.getPossibleInstantsForReturns.push(retval);
+ this.iteratorExhausted.push(false);
+ return {
+ callIndex: this.getPossibleInstantsForCallCount - 1,
+ timeZone: this,
+ *[Symbol.iterator]() {
+ yield* this.timeZone.getPossibleInstantsForReturns[this.callIndex];
+ this.timeZone.iteratorExhausted[this.callIndex] = true;
+ },
+ };
+ }
+ }
+
+ const timeZone = new TimeZonePossibleInstantsIterable();
+ func(timeZone);
+
+ assert.sameValue(timeZone.getPossibleInstantsForCallCount, expected.length, "getPossibleInstantsFor() method called correct number of times");
+
+ for (let index = 0; index < expected.length; index++) {
+ assert.sameValue(timeZone.getPossibleInstantsForCalledWith[index].toString(), expected[index], "getPossibleInstantsFor() called with expected PlainDateTime");
+ assert(timeZone.iteratorExhausted[index], "iterated through the whole iterable");
+ }
+ },
+
/*
* Check that any calendar-carrying Temporal object has its [[Calendar]]
* internal slot read by ToTemporalCalendar, and does not fetch the calendar
@@ -159,6 +816,428 @@ var TemporalHelpers = {
});
},
+ checkToTemporalInstantFastPath(func) {
+ const actual = [];
+ const expected = [];
+
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ Object.defineProperty(datetime, 'toString', {
+ get() {
+ actual.push("get toString");
+ return function (options) {
+ actual.push("call toString");
+ return Temporal.ZonedDateTime.prototype.toString.call(this, options);
+ };
+ },
+ });
+
+ func(datetime);
+ assert.compareArray(actual, expected, "toString not called");
+ },
+
+ checkToTemporalPlainDateTimeFastPath(func) {
+ const actual = [];
+ const expected = [];
+
+ const calendar = new Temporal.Calendar("iso8601");
+ const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+ const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDate.prototype);
+ ["year", "month", "monthCode", "day"].forEach((property) => {
+ Object.defineProperty(date, property, {
+ get() {
+ actual.push(`get ${property}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return TemporalHelpers.toPrimitiveObserver(actual, value, property);
+ },
+ });
+ });
+ ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => {
+ Object.defineProperty(date, property, {
+ get() {
+ actual.push(`get ${property}`);
+ return undefined;
+ },
+ });
+ });
+ Object.defineProperty(date, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ func(date, calendar);
+ assert.compareArray(actual, expected, "property getters not called");
+ },
+
+ /*
+ * A custom calendar that asserts its dateAdd() method is called with the
+ * options parameter having the value undefined.
+ */
+ calendarDateAddUndefinedOptions() {
+ class CalendarDateAddUndefinedOptions extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.dateAddCallCount = 0;
+ }
+
+ toString() {
+ return "dateadd-undef-options";
+ }
+
+ dateAdd(one, two, options) {
+ this.dateAddCallCount++;
+ assert.sameValue(options, undefined, "dateAdd shouldn't be called with options");
+ return super.dateAdd(one, two, options);
+ }
+ }
+ return new CalendarDateAddUndefinedOptions();
+ },
+
+ /*
+ * A custom calendar that returns @returnValue from its dateUntil() method,
+ * recording the call in @calls.
+ */
+ calendarDateUntilObservable(calls, returnValue) {
+ class CalendarDateUntilObservable extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateUntil() {
+ calls.push("call dateUntil");
+ return returnValue;
+ }
+ }
+
+ return new CalendarDateUntilObservable();
+ },
+
+ /*
+ * A custom calendar that returns an iterable instead of an array from its
+ * fields() method, otherwise identical to the ISO calendar.
+ */
+ calendarFieldsIterable() {
+ class CalendarFieldsIterable extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.fieldsCallCount = 0;
+ this.fieldsCalledWith = [];
+ this.iteratorExhausted = [];
+ }
+
+ toString() {
+ return "fields-iterable";
+ }
+
+ fields(fieldNames) {
+ this.fieldsCallCount++;
+ this.fieldsCalledWith.push(fieldNames.slice());
+ this.iteratorExhausted.push(false);
+ return {
+ callIndex: this.fieldsCallCount - 1,
+ calendar: this,
+ *[Symbol.iterator]() {
+ yield* this.calendar.fieldsCalledWith[this.callIndex];
+ this.calendar.iteratorExhausted[this.callIndex] = true;
+ },
+ };
+ }
+ }
+ return new CalendarFieldsIterable();
+ },
+
+ /*
+ * A custom calendar that modifies the fields object passed in to
+ * dateFromFields, sabotaging its time properties.
+ */
+ calendarMakeInfinityTime() {
+ class CalendarMakeInfinityTime extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateFromFields(fields, options) {
+ const retval = super.dateFromFields(fields, options);
+ fields.hour = Infinity;
+ fields.minute = Infinity;
+ fields.second = Infinity;
+ fields.millisecond = Infinity;
+ fields.microsecond = Infinity;
+ fields.nanosecond = Infinity;
+ return retval;
+ }
+ }
+ return new CalendarMakeInfinityTime();
+ },
+
+ /*
+ * A custom calendar that defines getters on the fields object passed into
+ * dateFromFields that throw, sabotaging its time properties.
+ */
+ calendarMakeInvalidGettersTime() {
+ class CalendarMakeInvalidGettersTime extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateFromFields(fields, options) {
+ const retval = super.dateFromFields(fields, options);
+ const throwingDescriptor = {
+ get() {
+ throw new Test262Error("reading a sabotaged time field");
+ },
+ };
+ Object.defineProperties(fields, {
+ hour: throwingDescriptor,
+ minute: throwingDescriptor,
+ second: throwingDescriptor,
+ millisecond: throwingDescriptor,
+ microsecond: throwingDescriptor,
+ nanosecond: throwingDescriptor,
+ });
+ return retval;
+ }
+ }
+ return new CalendarMakeInvalidGettersTime();
+ },
+
+ /*
+ * A custom calendar whose mergeFields() method returns a proxy object with
+ * all of its Get and HasProperty operations observable, as well as adding a
+ * "shouldNotBeCopied": true property.
+ */
+ calendarMergeFieldsGetters() {
+ class CalendarMergeFieldsGetters extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.mergeFieldsReturnOperations = [];
+ }
+
+ toString() {
+ return "merge-fields-getters";
+ }
+
+ dateFromFields(fields, options) {
+ assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
+ return super.dateFromFields(fields, options);
+ }
+
+ yearMonthFromFields(fields, options) {
+ assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
+ return super.yearMonthFromFields(fields, options);
+ }
+
+ monthDayFromFields(fields, options) {
+ assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
+ return super.monthDayFromFields(fields, options);
+ }
+
+ mergeFields(fields, additionalFields) {
+ const retval = super.mergeFields(fields, additionalFields);
+ retval._calendar = this;
+ retval.shouldNotBeCopied = true;
+ return new Proxy(retval, {
+ get(target, key) {
+ target._calendar.mergeFieldsReturnOperations.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(target._calendar.mergeFieldsReturnOperations, result, key);
+ },
+ has(target, key) {
+ target._calendar.mergeFieldsReturnOperations.push(`has ${key}`);
+ return key in target;
+ },
+ });
+ }
+ }
+ return new CalendarMergeFieldsGetters();
+ },
+
+ /*
+ * A custom calendar whose mergeFields() method returns a primitive value,
+ * given by @primitive, and which records the number of calls made to its
+ * dateFromFields(), yearMonthFromFields(), and monthDayFromFields() methods.
+ */
+ calendarMergeFieldsReturnsPrimitive(primitive) {
+ class CalendarMergeFieldsPrimitive extends Temporal.Calendar {
+ constructor(mergeFieldsReturnValue) {
+ super("iso8601");
+ this._mergeFieldsReturnValue = mergeFieldsReturnValue;
+ this.dateFromFieldsCallCount = 0;
+ this.monthDayFromFieldsCallCount = 0;
+ this.yearMonthFromFieldsCallCount = 0;
+ }
+
+ toString() {
+ return "merge-fields-primitive";
+ }
+
+ dateFromFields(fields, options) {
+ this.dateFromFieldsCallCount++;
+ return super.dateFromFields(fields, options);
+ }
+
+ yearMonthFromFields(fields, options) {
+ this.yearMonthFromFieldsCallCount++;
+ return super.yearMonthFromFields(fields, options);
+ }
+
+ monthDayFromFields(fields, options) {
+ this.monthDayFromFieldsCallCount++;
+ return super.monthDayFromFields(fields, options);
+ }
+
+ mergeFields() {
+ return this._mergeFieldsReturnValue;
+ }
+ }
+ return new CalendarMergeFieldsPrimitive(primitive);
+ },
+
+ /*
+ * observeProperty(calls, object, propertyName, value):
+ *
+ * Defines an own property @object.@propertyName with value @value, that
+ * will log any calls to its accessors to the array @calls.
+ */
+ observeProperty(calls, object, propertyName, value) {
+ Object.defineProperty(object, propertyName, {
+ get() {
+ calls.push(`get ${propertyName}`);
+ return value;
+ },
+ set(v) {
+ calls.push(`set ${propertyName}`);
+ }
+ });
+ },
+
+ /*
+ * A custom calendar that does not allow any of its methods to be called, for
+ * the purpose of asserting that a particular operation does not call into
+ * user code.
+ */
+ calendarThrowEverything() {
+ class CalendarThrowEverything extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ toString() {
+ TemporalHelpers.assertUnreachable("toString should not be called");
+ }
+ dateFromFields() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be called");
+ }
+ yearMonthFromFields() {
+ TemporalHelpers.assertUnreachable("yearMonthFromFields should not be called");
+ }
+ monthDayFromFields() {
+ TemporalHelpers.assertUnreachable("monthDayFromFields should not be called");
+ }
+ dateAdd() {
+ TemporalHelpers.assertUnreachable("dateAdd should not be called");
+ }
+ dateUntil() {
+ TemporalHelpers.assertUnreachable("dateUntil should not be called");
+ }
+ era() {
+ TemporalHelpers.assertUnreachable("era should not be called");
+ }
+ eraYear() {
+ TemporalHelpers.assertUnreachable("eraYear should not be called");
+ }
+ year() {
+ TemporalHelpers.assertUnreachable("year should not be called");
+ }
+ month() {
+ TemporalHelpers.assertUnreachable("month should not be called");
+ }
+ monthCode() {
+ TemporalHelpers.assertUnreachable("monthCode should not be called");
+ }
+ day() {
+ TemporalHelpers.assertUnreachable("day should not be called");
+ }
+ fields() {
+ TemporalHelpers.assertUnreachable("fields should not be called");
+ }
+ mergeFields() {
+ TemporalHelpers.assertUnreachable("mergeFields should not be called");
+ }
+ }
+
+ return new CalendarThrowEverything();
+ },
+
+ /*
+ * oneShiftTimeZone(shiftInstant, shiftNanoseconds):
+ *
+ * In the case of a spring-forward time zone offset transition (skipped time),
+ * and disambiguation === 'earlier', BuiltinTimeZoneGetInstantFor subtracts a
+ * negative number of nanoseconds from a PlainDateTime, which should balance
+ * with the microseconds field.
+ *
+ * This returns an instance of a custom time zone class which skips a length
+ * of time equal to shiftNanoseconds (a number), at the Temporal.Instant
+ * shiftInstant. Before shiftInstant, it's identical to UTC, and after
+ * shiftInstant it's a constant-offset time zone.
+ *
+ * It provides a getPossibleInstantsForCalledWith member which is an array
+ * with the result of calling toString() on any PlainDateTimes passed to
+ * getPossibleInstantsFor().
+ */
+ oneShiftTimeZone(shiftInstant, shiftNanoseconds) {
+ class OneShiftTimeZone extends Temporal.TimeZone {
+ constructor(shiftInstant, shiftNanoseconds) {
+ super("+00:00");
+ this._shiftInstant = shiftInstant;
+ this._epoch1 = shiftInstant.epochNanoseconds;
+ this._epoch2 = this._epoch1 + BigInt(shiftNanoseconds);
+ this._shiftNanoseconds = shiftNanoseconds;
+ this._shift = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, this._shiftNanoseconds);
+ this.getPossibleInstantsForCalledWith = [];
+ }
+
+ _isBeforeShift(instant) {
+ return instant.epochNanoseconds < this._epoch1;
+ }
+
+ getOffsetNanosecondsFor(instant) {
+ return this._isBeforeShift(instant) ? 0 : this._shiftNanoseconds;
+ }
+
+ getPossibleInstantsFor(plainDateTime) {
+ this.getPossibleInstantsForCalledWith.push(plainDateTime.toString());
+ const [instant] = super.getPossibleInstantsFor(plainDateTime);
+ if (this._shiftNanoseconds > 0) {
+ if (this._isBeforeShift(instant)) return [instant];
+ if (instant.epochNanoseconds < this._epoch2) return [];
+ return [instant.add(this._shift)];
+ }
+ if (instant.epochNanoseconds < this._epoch2) return [instant];
+ const shifted = instant.add(this._shift);
+ if (this._isBeforeShift(instant)) return [instant, shifted];
+ return [shifted];
+ }
+
+ getNextTransition(instant) {
+ return this._isBeforeShift(instant) ? this._shiftInstant : null;
+ }
+
+ getPreviousTransition(instant) {
+ return this._isBeforeShift(instant) ? null : this._shiftInstant;
+ }
+
+ toString() {
+ return "Custom/One_Shift";
+ }
+ }
+ return new OneShiftTimeZone(shiftInstant, shiftNanoseconds);
+ },
+
/*
* specificOffsetTimeZone():
*
@@ -187,4 +1266,96 @@ var TemporalHelpers = {
}
return new SpecificOffsetTimeZone(offsetValue);
},
+
+ /*
+ * springForwardFallBackTimeZone():
+ *
+ * This returns an instance of a custom time zone class that implements one
+ * single spring-forward/fall-back transition, for the purpose of testing the
+ * disambiguation option, without depending on system time zone data.
+ *
+ * The spring-forward occurs at epoch second 954669600 (2000-04-02T02:00
+ * local) and goes from offset -08:00 to -07:00.
+ *
+ * The fall-back occurs at epoch second 972810000 (2000-10-29T02:00 local) and
+ * goes from offset -07:00 to -08:00.
+ */
+ springForwardFallBackTimeZone() {
+ const { compare } = Temporal.PlainDateTime;
+ const springForwardLocal = new Temporal.PlainDateTime(2000, 4, 2, 2);
+ const springForwardEpoch = 954669600_000_000_000n;
+ const fallBackLocal = new Temporal.PlainDateTime(2000, 10, 29, 1);
+ const fallBackEpoch = 972810000_000_000_000n;
+ const winterOffset = new Temporal.TimeZone('-08:00');
+ const summerOffset = new Temporal.TimeZone('-07:00');
+
+ class SpringForwardFallBackTimeZone extends Temporal.TimeZone {
+ constructor() {
+ super("-08:00");
+ }
+
+ getOffsetNanosecondsFor(instant) {
+ if (instant.epochNanoseconds < springForwardEpoch ||
+ instant.epochNanoseconds >= fallBackEpoch) {
+ return winterOffset.getOffsetNanosecondsFor(instant);
+ }
+ return summerOffset.getOffsetNanosecondsFor(instant);
+ }
+
+ getPossibleInstantsFor(datetime) {
+ if (compare(datetime, springForwardLocal) >= 0 && compare(datetime, springForwardLocal.add({ hours: 1 })) < 0) {
+ return [];
+ }
+ if (compare(datetime, fallBackLocal) >= 0 && compare(datetime, fallBackLocal.add({ hours: 1 })) < 0) {
+ return [summerOffset.getInstantFor(datetime), winterOffset.getInstantFor(datetime)];
+ }
+ if (compare(datetime, springForwardLocal) < 0 || compare(datetime, fallBackLocal) >= 0) {
+ return [winterOffset.getInstantFor(datetime)];
+ }
+ return [summerOffset.getInstantFor(datetime)];
+ }
+
+ getPreviousTransition(instant) {
+ if (instant.epochNanoseconds > fallBackEpoch) return new Temporal.Instant(fallBackEpoch);
+ if (instant.epochNanoseconds > springForwardEpoch) return new Temporal.Instant(springForwardEpoch);
+ return null;
+ }
+
+ getNextTransition(instant) {
+ if (instant.epochNanoseconds < springForwardEpoch) return new Temporal.Instant(springForwardEpoch);
+ if (instant.epochNanoseconds < fallBackEpoch) return new Temporal.Instant(fallBackEpoch);
+ return null;
+ }
+
+ toString() {
+ return "Custom/Spring_Fall";
+ }
+ }
+ return new SpringForwardFallBackTimeZone();
+ },
+
+ /*
+ * Returns an object that will append logs of any Gets or Calls of its valueOf
+ * or toString properties to the array calls. Both valueOf and toString will
+ * return the actual primitiveValue. propertyName is used in the log.
+ */
+ toPrimitiveObserver(calls, primitiveValue, propertyName) {
+ return {
+ get valueOf() {
+ calls.push(`get ${propertyName}.valueOf`);
+ return function () {
+ calls.push(`call ${propertyName}.valueOf`);
+ return primitiveValue;
+ };
+ },
+ get toString() {
+ calls.push(`get ${propertyName}.toString`);
+ return function () {
+ calls.push(`call ${propertyName}.toString`);
+ if (primitiveValue === undefined) return undefined;
+ return primitiveValue.toString();
+ };
+ },
+ };
+ },
};
diff --git a/test/built-ins/Temporal/Calendar/builtin.js b/test/built-ins/Temporal/Calendar/builtin.js
new file mode 100644
index 00000000000..f77ec2bfd5e
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/builtin.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar
+description: Tests that Temporal.Calendar meets the requirements for built-in objects
+info: |
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.Calendar.prototype,
+ "object", "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/constructor.js b/test/built-ins/Temporal/Calendar/constructor.js
new file mode 100644
index 00000000000..461b9ece2c8
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/constructor.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar
+description: Temporal.Calendar constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.Calendar());
diff --git a/test/built-ins/Temporal/Calendar/from/builtin.js b/test/built-ins/Temporal/Calendar/from/builtin.js
new file mode 100644
index 00000000000..4345b708d68
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/from/builtin.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.from
+description: Tests that Temporal.Calendar.from meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.from.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/from/calendar-object-invalid.js b/test/built-ins/Temporal/Calendar/from/calendar-object-invalid.js
new file mode 100644
index 00000000000..fe70acdc985
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/from/calendar-object-invalid.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.from
+description: Converting objects to Temporal.Calendar
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => Temporal.Calendar.from({ calendar: "local" }));
+assert.throws(RangeError, () => Temporal.Calendar.from({ calendar: { calendar: "iso8601" } }));
diff --git a/test/built-ins/Temporal/Calendar/from/calendar-object-operations.js b/test/built-ins/Temporal/Calendar/from/calendar-object-operations.js
new file mode 100644
index 00000000000..a11df98aa14
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/from/calendar-object-operations.js
@@ -0,0 +1,39 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.from
+description: Converting objects to Temporal.Calendar
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "has outer.calendar",
+ "get outer.calendar",
+ "has inner.calendar",
+ "get inner.toString",
+ "call inner.toString",
+];
+const actual = [];
+const calendar = new Proxy({}, {
+ has(t, p) {
+ actual.push(`has outer.${p}`);
+ return true;
+ },
+ get(t, p) {
+ actual.push(`get outer.${p}`);
+ return new Proxy(TemporalHelpers.toPrimitiveObserver(actual, "iso8601", "inner"), {
+ has(t, p) {
+ actual.push(`has inner.${p}`);
+ return true;
+ },
+ get(t, p) {
+ return t[p];
+ },
+ });
+ },
+});
+const result = Temporal.Calendar.from(calendar);
+assert.sameValue(result.id, "iso8601");
+assert.compareArray(actual, expected);
diff --git a/test/built-ins/Temporal/Calendar/from/calendar-object.js b/test/built-ins/Temporal/Calendar/from/calendar-object.js
new file mode 100644
index 00000000000..cb680a26d8c
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/from/calendar-object.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.from
+description: Converting objects to Temporal.Calendar
+features: [Temporal]
+---*/
+
+const cal = new Temporal.Calendar("iso8601");
+const calFromObject = Temporal.Calendar.from({ calendar: cal });
+assert(calFromObject instanceof Temporal.Calendar);
+assert.sameValue(calFromObject.id, "iso8601");
+
+const calFromString = Temporal.Calendar.from({ calendar: "iso8601" });
+assert(calFromString instanceof Temporal.Calendar);
+assert.sameValue(calFromString.id, "iso8601");
+
+const custom = { id: "custom-calendar" };
+assert.sameValue(Temporal.Calendar.from({ calendar: custom }), custom);
+assert.sameValue(Temporal.Calendar.from(custom), custom);
diff --git a/test/built-ins/Temporal/Calendar/from/calendar-string-builtin.js b/test/built-ins/Temporal/Calendar/from/calendar-string-builtin.js
new file mode 100644
index 00000000000..f830c814fd9
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/from/calendar-string-builtin.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.from
+description: Calendar.from should support iso8601.
+features: [Temporal]
+---*/
+
+const tests = [
+ "iso8601",
+ "1994-11-05T08:15:30-05:00",
+];
+
+for (const item of tests) {
+ const calendar = Temporal.Calendar.from(item);
+ assert(calendar instanceof Temporal.Calendar);
+ assert.sameValue(calendar.id, "iso8601");
+}
diff --git a/test/built-ins/Temporal/Calendar/from/calendar-string-not-builtin.js b/test/built-ins/Temporal/Calendar/from/calendar-string-not-builtin.js
new file mode 100644
index 00000000000..c8f7a601d7c
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/from/calendar-string-not-builtin.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.from
+description: from() throws if the argument is not a built-in calendar name.
+features: [Temporal]
+---*/
+
+const tests = [
+ "local",
+ "iso-8601",
+ "[u-ca=iso8601]",
+ "invalid-calendar",
+];
+
+for (const item of tests) {
+ assert.throws(RangeError, () => Temporal.Calendar.from(item));
+}
diff --git a/test/built-ins/Temporal/Calendar/from/calendar-temporal-object.js b/test/built-ins/Temporal/Calendar/from/calendar-temporal-object.js
new file mode 100644
index 00000000000..d56c7820fd8
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/from/calendar-temporal-object.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.from
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.from step 1:
+ 1. Return ? ToTemporalCalendar(_item_).
+ 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 newCalendar = Temporal.Calendar.from(temporalObject);
+ assert.sameValue(newCalendar, calendar, "calendar object retrieved from internal slot");
+});
diff --git a/test/built-ins/Temporal/Calendar/from/length.js b/test/built-ins/Temporal/Calendar/from/length.js
new file mode 100644
index 00000000000..154ba81099a
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/from/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.from
+description: Temporal.Calendar.from.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/from/name.js b/test/built-ins/Temporal/Calendar/from/name.js
new file mode 100644
index 00000000000..16c0f551697
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/from/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.from
+description: Temporal.Calendar.from.name is "from"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/from/not-a-constructor.js b/test/built-ins/Temporal/Calendar/from/not-a-constructor.js
new file mode 100644
index 00000000000..01266cddc8d
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/from/not-a-constructor.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.from
+description: Temporal.Calendar.from does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.from), false,
+ "isConstructor(Temporal.Calendar.from)");
diff --git a/test/built-ins/Temporal/Calendar/from/prop-desc.js b/test/built-ins/Temporal/Calendar/from/prop-desc.js
new file mode 100644
index 00000000000..53ae121c6ca
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/from/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.from
+description: The "from" property of Temporal.Calendar
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.from,
+ "function",
+ "`typeof Calendar.from` is `function`"
+);
+
+verifyProperty(Temporal.Calendar, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/from/subclassing-ignored.js b/test/built-ins/Temporal/Calendar/from/subclassing-ignored.js
new file mode 100644
index 00000000000..738afa1fd29
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/from/subclassing-ignored.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.Calendar,
+ "from",
+ ["iso8601"],
+ (result) => {
+ assert.sameValue(result.id, "iso8601", "id property of result");
+ assert.sameValue(result.toString(), "iso8601", "toString() of result");
+ },
+);
diff --git a/test/built-ins/Temporal/Calendar/length.js b/test/built-ins/Temporal/Calendar/length.js
new file mode 100644
index 00000000000..0c3c834213e
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar
+description: Temporal.Calendar.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/missing-arguments.js b/test/built-ins/Temporal/Calendar/missing-arguments.js
new file mode 100644
index 00000000000..ce0dd2fdbdf
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/missing-arguments.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar
+description: RangeError thrown when constructor invoked with no argument
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.Calendar());
+assert.throws(RangeError, () => new Temporal.Calendar(undefined));
diff --git a/test/built-ins/Temporal/Calendar/name.js b/test/built-ins/Temporal/Calendar/name.js
new file mode 100644
index 00000000000..9ea1492e0d2
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar
+description: Temporal.Calendar.name is "Calendar"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar, "name", {
+ value: "Calendar",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prop-desc.js b/test/built-ins/Temporal/Calendar/prop-desc.js
new file mode 100644
index 00000000000..fbb298626a5
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar
+description: The "Calendar" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar,
+ "function",
+ "`typeof Calendar` is `function`"
+);
+
+verifyProperty(Temporal, "Calendar", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateAdd/argument-plaindatetime.js b/test/built-ins/Temporal/Calendar/prototype/dateAdd/argument-plaindatetime.js
new file mode 100644
index 00000000000..9683842c380
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateAdd/argument-plaindatetime.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.dateadd step 4:
+ 4. Set _date_ to ? ToTemporalDate(_date_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ const duration = new Temporal.Duration(0, 1);
+ const result = calendar.dateAdd(datetime, duration);
+ TemporalHelpers.assertPlainDate(result, 2000, 6, "M06", 2);
+ assert.sameValue(result.hour, undefined, "instance of PlainDate returned, not PlainDateTime");
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..afa8fda9746
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const duration = new Temporal.Duration(1);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.dateAdd(datetime, duration));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..7a67d15bfb6
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const duration = new Temporal.Duration(1);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.dateAdd(datetime, duration));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..aa2c986058e
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateAdd/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const duration = new Temporal.Duration(1);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.dateAdd(datetime, duration));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateAdd/balance-smaller-units.js b/test/built-ins/Temporal/Calendar/prototype/dateAdd/balance-smaller-units.js
new file mode 100644
index 00000000000..c2898cfac4e
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateAdd/balance-smaller-units.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Durations with units smaller than days are balanced before adding
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+const duration = new Temporal.Duration(0, 0, 0, 1, 24, 1440, 86400, 86400_000, 86400_000_000, 86400_000_000_000);
+
+const result = calendar.dateAdd(date, duration);
+TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 9, "units smaller than days are balanced");
+
+const resultString = calendar.dateAdd(date, "P1DT24H1440M86400S");
+TemporalHelpers.assertPlainDate(resultString, 2000, 5, "M05", 6, "units smaller than days are balanced");
+
+const resultPropBag = calendar.dateAdd(date, { days: 1, hours: 24, minutes: 1440, seconds: 86400, milliseconds: 86400_000, microseconds: 86400_000_000, nanoseconds: 86400_000_000_000 });
+TemporalHelpers.assertPlainDate(resultPropBag, 2000, 5, "M05", 9, "units smaller than days are balanced");
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateAdd/basic.js b/test/built-ins/Temporal/Calendar/prototype/dateAdd/basic.js
new file mode 100644
index 00000000000..c1f8ad510c0
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateAdd/basic.js
@@ -0,0 +1,50 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Basic tests for dateAdd().
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const date = Temporal.PlainDate.from("1994-11-06");
+const positiveDuration = Temporal.Duration.from({ months: 1, weeks: 1 });
+const negativeDuration = Temporal.Duration.from({ months: -1, weeks: -1 });
+
+TemporalHelpers.assertPlainDate(
+ iso.dateAdd(Temporal.PlainDateTime.from("1994-11-06T08:15:30"), positiveDuration, {}),
+ 1994, 12, "M12", 13, "date: PlainDateTime");
+
+TemporalHelpers.assertPlainDate(
+ iso.dateAdd({ year: 1994, month: 11, day: 6 }, positiveDuration, {}),
+ 1994, 12, "M12", 13, "date: property bag");
+
+TemporalHelpers.assertPlainDate(
+ iso.dateAdd("1994-11-06", positiveDuration, {}),
+ 1994, 12, "M12", 13, "date: string");
+
+assert.throws(TypeError, () => iso.dateAdd({ month: 11 }, positiveDuration, {}), "date: missing property");
+
+TemporalHelpers.assertPlainDate(
+ iso.dateAdd(date, { months: 1, weeks: 1 }, {}),
+ 1994, 12, "M12", 13, "duration: property bag");
+
+TemporalHelpers.assertPlainDate(
+ iso.dateAdd(date, "P1M1W", {}),
+ 1994, 12, "M12", 13, "duration: string");
+
+assert.throws(TypeError, () => iso.dateAdd(date, { month: 1 }, {}), "duration: missing property");
+
+TemporalHelpers.assertPlainDate(
+ iso.dateAdd(Temporal.PlainDateTime.from("1994-11-06T08:15:30"), negativeDuration, {}),
+ 1994, 9, "M09", 29, "date: PlainDateTime, negative duration");
+
+TemporalHelpers.assertPlainDate(
+ iso.dateAdd({ year: 1994, month: 11, day: 6 }, negativeDuration, {}),
+ 1994, 9, "M09", 29, "date: property bag, negative duration");
+
+TemporalHelpers.assertPlainDate(
+ iso.dateAdd("1994-11-06", negativeDuration, {}),
+ 1994, 9, "M09", 29, "date: string, negative duration");
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateAdd/builtin.js b/test/built-ins/Temporal/Calendar/prototype/dateAdd/builtin.js
new file mode 100644
index 00000000000..0d8d3d99603
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateAdd/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: >
+ Tests that Temporal.Calendar.prototype.dateAdd
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.dateAdd),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.dateAdd),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.dateAdd),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.dateAdd.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateAdd/calendar-fields-iterable.js b/test/built-ins/Temporal/Calendar/prototype/dateAdd/calendar-fields-iterable.js
new file mode 100644
index 00000000000..da8453aea57
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateAdd/calendar-fields-iterable.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.dateadd step 4:
+ 4. Set _date_ to ? ToTemporalDate(_date_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const duration = new Temporal.Duration(0, 1);
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.dateAdd({ year: 2000, month: 5, day: 2, calendar: calendar2 }, duration);
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateAdd/calendar-temporal-object.js b/test/built-ins/Temporal/Calendar/prototype/dateAdd/calendar-temporal-object.js
new file mode 100644
index 00000000000..a230867c3df
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateAdd/calendar-temporal-object.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.dateadd step 4:
+ 4. Set _date_ to ? ToTemporalDate(_date_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ const duration = new Temporal.Duration(0, 1);
+ calendar.dateAdd({ year: 2000, month: 5, day: 2, calendar: temporalObject }, duration);
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateAdd/date-infinity-throws-rangeerror.js b/test/built-ins/Temporal/Calendar/prototype/dateAdd/date-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..4ad162032aa
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateAdd/date-infinity-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.dateadd
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const duration = new Temporal.Duration(1);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.dateAdd({ ...base, [prop]: inf }, duration, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.dateAdd({ ...base, [prop]: obj }, duration, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateAdd/duration-argument-string-negative-fractional-units.js b/test/built-ins/Temporal/Calendar/prototype/dateAdd/duration-argument-string-negative-fractional-units.js
new file mode 100644
index 00000000000..7fb804b0a6b
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateAdd/duration-argument-string-negative-fractional-units.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+
+const resultHours = calendar.dateAdd(instance, "-PT24.567890123H");
+TemporalHelpers.assertPlainDate(resultHours, 2000, 5, "M05", 1, "negative fractional hours");
+
+const resultMinutes = calendar.dateAdd(instance, "-PT1440.567890123M");
+TemporalHelpers.assertPlainDate(resultMinutes, 2000, 5, "M05", 1, "negative fractional minutes");
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateAdd/length.js b/test/built-ins/Temporal/Calendar/prototype/dateAdd/length.js
new file mode 100644
index 00000000000..acc31761c77
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateAdd/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Temporal.Calendar.prototype.dateAdd.length is 2
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.dateAdd, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateAdd/name.js b/test/built-ins/Temporal/Calendar/prototype/dateAdd/name.js
new file mode 100644
index 00000000000..e4d966254ec
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateAdd/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Temporal.Calendar.prototype.dateAdd.name is "dateAdd".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.dateAdd, "name", {
+ value: "dateAdd",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateAdd/not-a-constructor.js b/test/built-ins/Temporal/Calendar/prototype/dateAdd/not-a-constructor.js
new file mode 100644
index 00000000000..eb71ff8c00d
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateAdd/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: >
+ Temporal.Calendar.prototype.dateAdd does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.dateAdd();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.dateAdd), false,
+ "isConstructor(Temporal.Calendar.prototype.dateAdd)");
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateAdd/overflow-invalid-string.js b/test/built-ins/Temporal/Calendar/prototype/dateAdd/overflow-invalid-string.js
new file mode 100644
index 00000000000..004385da73a
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateAdd/overflow-invalid-string.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+const duration = new Temporal.Duration(3, 3, 0, 3);
+assert.throws(RangeError, () => calendar.dateAdd(date, duration, { overflow: "other string" }));
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateAdd/overflow-undefined.js b/test/built-ins/Temporal/Calendar/prototype/dateAdd/overflow-undefined.js
new file mode 100644
index 00000000000..453093bc579
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateAdd/overflow-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const date = new Temporal.PlainDate(2000, 5, 31, calendar);
+const duration = new Temporal.Duration(3, 1);
+
+const explicit = calendar.dateAdd(date, duration, { overflow: undefined });
+TemporalHelpers.assertPlainDate(explicit, 2003, 6, "M06", 30, "default overflow is constrain");
+const implicit = calendar.dateAdd(date, duration, {});
+TemporalHelpers.assertPlainDate(implicit, 2003, 6, "M06", 30, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateAdd/overflow-wrong-type.js b/test/built-ins/Temporal/Calendar/prototype/dateAdd/overflow-wrong-type.js
new file mode 100644
index 00000000000..945a720f253
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateAdd/overflow-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+const duration = new Temporal.Duration(3, 3, 0, 3);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => calendar.dateAdd(date, duration, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDate(result, 2003, 8, "M08", 5, descr),
+);
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateAdd/prop-desc.js b/test/built-ins/Temporal/Calendar/prototype/dateAdd/prop-desc.js
new file mode 100644
index 00000000000..6ae28e706aa
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateAdd/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: The "dateAdd" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.dateAdd,
+ "function",
+ "`typeof Calendar.prototype.dateAdd` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "dateAdd", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateFromFields/builtin.js b/test/built-ins/Temporal/Calendar/prototype/dateFromFields/builtin.js
new file mode 100644
index 00000000000..11d9635a51d
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateFromFields/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.datefromfields
+description: >
+ Tests that Temporal.Calendar.prototype.dateFromFields
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.dateFromFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.dateFromFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.dateFromFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.dateFromFields.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateFromFields/fields-not-object.js b/test/built-ins/Temporal/Calendar/prototype/dateFromFields/fields-not-object.js
new file mode 100644
index 00000000000..97ce2e33433
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateFromFields/fields-not-object.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.datefromfields
+description: Throw a TypeError if the fields is not an object
+features: [Symbol, Temporal]
+---*/
+
+const tests = [undefined, null, false, "string", Symbol("sym"), Math.PI, 42n];
+const iso = Temporal.Calendar.from("iso8601");
+for (const fields of tests) {
+ assert.throws(TypeError, () => iso.dateFromFields(fields, {}));
+}
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateFromFields/infinity-throws-rangeerror.js b/test/built-ins/Temporal/Calendar/prototype/dateFromFields/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..7083f473996
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateFromFields/infinity-throws-rangeerror.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.datefromfields
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.dateFromFields({ ...base, [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.dateFromFields({ ...base, [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateFromFields/length.js b/test/built-ins/Temporal/Calendar/prototype/dateFromFields/length.js
new file mode 100644
index 00000000000..c03bcfc7315
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateFromFields/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.datefromfields
+description: Temporal.Calendar.prototype.dateFromFields.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.dateFromFields, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateFromFields/name.js b/test/built-ins/Temporal/Calendar/prototype/dateFromFields/name.js
new file mode 100644
index 00000000000..35ea11bcd53
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateFromFields/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.datefromfields
+description: Temporal.Calendar.prototype.dateFromFields.name is "dateFromFields".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.dateFromFields, "name", {
+ value: "dateFromFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateFromFields/not-a-constructor.js b/test/built-ins/Temporal/Calendar/prototype/dateFromFields/not-a-constructor.js
new file mode 100644
index 00000000000..aca5ec28165
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateFromFields/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.datefromfields
+description: >
+ Temporal.Calendar.prototype.dateFromFields does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.dateFromFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.dateFromFields), false,
+ "isConstructor(Temporal.Calendar.prototype.dateFromFields)");
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateFromFields/overflow-invalid-string.js b/test/built-ins/Temporal/Calendar/prototype/dateFromFields/overflow-invalid-string.js
new file mode 100644
index 00000000000..76bdfc89821
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateFromFields/overflow-invalid-string.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.datefromfields
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isodatefromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.calendar.prototype.datefromfields step 6:
+ 6. Let _result_ be ? ISODateFromFields(_fields_, _options_).
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+assert.throws(RangeError, () => calendar.dateFromFields({ year: 2000, month: 5, day: 2 }, { overflow: "other string" }));
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateFromFields/overflow-undefined.js b/test/built-ins/Temporal/Calendar/prototype/dateFromFields/overflow-undefined.js
new file mode 100644
index 00000000000..841cebd9518
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateFromFields/overflow-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.datefromfields
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isodatefromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.calendar.prototype.datefromfields step 6:
+ 6. Let _result_ be ? ISODateFromFields(_fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+
+const explicit = calendar.dateFromFields({ year: 2000, month: 15, day: 2 }, { overflow: undefined });
+TemporalHelpers.assertPlainDate(explicit, 2000, 12, "M12", 2, "default overflow is constrain");
+const implicit = calendar.dateFromFields({ year: 2000, month: 15, day: 2 }, {});
+TemporalHelpers.assertPlainDate(implicit, 2000, 12, "M12", 2, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateFromFields/overflow-wrong-type.js b/test/built-ins/Temporal/Calendar/prototype/dateFromFields/overflow-wrong-type.js
new file mode 100644
index 00000000000..41adc3d46e3
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateFromFields/overflow-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.datefromfields
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isodatefromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.calendar.prototype.datefromfields step 6:
+ 6. Let _result_ be ? ISODateFromFields(_fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => calendar.dateFromFields({ year: 2000, month: 5, day: 2 }, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 2, descr),
+);
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateFromFields/prop-desc.js b/test/built-ins/Temporal/Calendar/prototype/dateFromFields/prop-desc.js
new file mode 100644
index 00000000000..886e3a42ac5
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateFromFields/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.datefromfields
+description: The "dateFromFields" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.dateFromFields,
+ "function",
+ "`typeof Calendar.prototype.dateFromFields` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "dateFromFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateUntil/argument-infinity-throws-rangeerror.js b/test/built-ins/Temporal/Calendar/prototype/dateUntil/argument-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..9f8cec10b7e
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateUntil/argument-infinity-throws-rangeerror.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in a property bag for either argument is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.dateuntil
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const other = new Temporal.PlainDate(2001, 6, 3);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.dateUntil({ ...base, [prop]: inf }, other), `${prop} property cannot be ${inf}`);
+
+ assert.throws(RangeError, () => instance.dateUntil(other, { ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls1 = [];
+ const obj1 = TemporalHelpers.toPrimitiveObserver(calls1, inf, prop);
+ assert.throws(RangeError, () => instance.dateUntil({ ...base, [prop]: obj1 }, other));
+ assert.compareArray(calls1, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+
+ const calls2 = [];
+ const obj2 = TemporalHelpers.toPrimitiveObserver(calls2, inf, prop);
+ assert.throws(RangeError, () => instance.dateUntil(other, { ...base, [prop]: obj2 }));
+ assert.compareArray(calls2, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateUntil/argument-plaindatetime.js b/test/built-ins/Temporal/Calendar/prototype/dateUntil/argument-plaindatetime.js
new file mode 100644
index 00000000000..29cbe4481ed
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateUntil/argument-plaindatetime.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateuntil
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.dateuntil steps 4–5:
+ 4. Set _one_ to ? ToTemporalDate(_one_).
+ 5. Set _two_ to ? ToTemporalDate(_two_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ const result = calendar.dateUntil(datetime, date);
+ assert.sameValue(result.total({ unit: "nanoseconds" }), 0, "time part dropped");
+});
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ const result = calendar.dateUntil(date, datetime);
+ assert.sameValue(result.total({ unit: "nanoseconds" }), 0, "time part dropped");
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..accc9ceb667
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateuntil
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, Infinity, -Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+
+ assert.throws(RangeError, () => calendar.dateUntil(datetime, date));
+ assert.throws(RangeError, () => calendar.dateUntil(date, datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..d33b0805c02
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateuntil
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+
+ assert.throws(RangeError, () => calendar.dateUntil(datetime, date));
+ assert.throws(RangeError, () => calendar.dateUntil(date, datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..6d283f4a15f
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateUntil/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateuntil
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+
+ assert.throws(TypeError, () => calendar.dateUntil(datetime, date));
+ assert.throws(TypeError, () => calendar.dateUntil(date, datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateUntil/basic.js b/test/built-ins/Temporal/Calendar/prototype/dateUntil/basic.js
new file mode 100644
index 00000000000..c7d4d4b49f0
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateUntil/basic.js
@@ -0,0 +1,45 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateuntil
+description: Basic tests for dateUntil().
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const date1 = Temporal.PlainDate.from("1999-09-03");
+const date2 = Temporal.PlainDate.from("2000-01-01");
+
+TemporalHelpers.assertDuration(
+ iso.dateUntil(date1, date2, {}),
+ 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, "two PlainDates");
+
+TemporalHelpers.assertDuration(
+ iso.dateUntil(Temporal.PlainDateTime.from("1999-09-03T08:15:30"), date2, {}),
+ 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, "first argument: PlainDateTime");
+
+TemporalHelpers.assertDuration(
+ iso.dateUntil({ year: 1999, month: 9, day: 3 }, date2, {}),
+ 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, "first argument: property bag");
+
+TemporalHelpers.assertDuration(
+ iso.dateUntil("1999-09-03", date2, {}),
+ 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, "first argument: string");
+
+assert.throws(TypeError, () => iso.dateUntil({ month: 11 }, date2, {}), "first argument: missing property");
+
+TemporalHelpers.assertDuration(
+ iso.dateUntil(date1, Temporal.PlainDateTime.from("2000-01-01T08:15:30"), {}),
+ 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, "second argument: PlainDateTime");
+
+TemporalHelpers.assertDuration(
+ iso.dateUntil(date1, { year: 2000, month: 1, day: 1 }, {}),
+ 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, "second argument: property bag");
+
+TemporalHelpers.assertDuration(
+ iso.dateUntil(date1, "2000-01-01", {}),
+ 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, "second argument: string");
+
+assert.throws(TypeError, () => iso.dateUntil(date1, { month: 11 }, {}), "second argument: missing property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateUntil/builtin.js b/test/built-ins/Temporal/Calendar/prototype/dateUntil/builtin.js
new file mode 100644
index 00000000000..94ebc2df633
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateUntil/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateuntil
+description: >
+ Tests that Temporal.Calendar.prototype.dateUntil
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.dateUntil),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.dateUntil),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.dateUntil),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.dateUntil.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateUntil/calendar-fields-iterable.js b/test/built-ins/Temporal/Calendar/prototype/dateUntil/calendar-fields-iterable.js
new file mode 100644
index 00000000000..a14e3fa7cac
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateUntil/calendar-fields-iterable.js
@@ -0,0 +1,40 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateuntil
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.dateuntil steps 4–5:
+ 4. Set _one_ to ? ToTemporalDate(_one_).
+ 5. Set _two_ to ? ToTemporalDate(_two_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+const calendar3 = TemporalHelpers.calendarFieldsIterable();
+calendar1.dateUntil(
+ { year: 2000, month: 5, day: 2, calendar: calendar2 },
+ { year: 2005, month: 6, day: 3, calendar: calendar3 },
+);
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.sameValue(calendar3.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
+assert.compareArray(calendar3.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar3.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateUntil/calendar-temporal-object.js b/test/built-ins/Temporal/Calendar/prototype/dateUntil/calendar-temporal-object.js
new file mode 100644
index 00000000000..57e94608a19
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateUntil/calendar-temporal-object.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateuntil
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.dateuntil steps 4–5:
+ 4. Set _one_ to ? ToTemporalDate(_one_).
+ 5. Set _two_ to ? ToTemporalDate(_two_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.dateUntil(
+ { year: 2000, month: 5, day: 2, calendar: temporalObject },
+ { year: 2005, month: 6, day: 3, calendar: temporalObject },
+ );
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateUntil/largestunit-plurals-accepted.js b/test/built-ins/Temporal/Calendar/prototype/dateUntil/largestunit-plurals-accepted.js
new file mode 100644
index 00000000000..45048044742
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateUntil/largestunit-plurals-accepted.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateuntil
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 12);
+const calendar = new Temporal.Calendar("iso8601");
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => calendar.dateUntil(earlier, later, { largestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateUntil/length.js b/test/built-ins/Temporal/Calendar/prototype/dateUntil/length.js
new file mode 100644
index 00000000000..30c15f19ada
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateUntil/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateuntil
+description: Temporal.Calendar.prototype.dateUntil.length is 2
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.dateUntil, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateUntil/name.js b/test/built-ins/Temporal/Calendar/prototype/dateUntil/name.js
new file mode 100644
index 00000000000..c648ae3738e
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateUntil/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateuntil
+description: Temporal.Calendar.prototype.dateUntil.name is "dateUntil".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.dateUntil, "name", {
+ value: "dateUntil",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateUntil/not-a-constructor.js b/test/built-ins/Temporal/Calendar/prototype/dateUntil/not-a-constructor.js
new file mode 100644
index 00000000000..c0293659bb2
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateUntil/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateuntil
+description: >
+ Temporal.Calendar.prototype.dateUntil does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.dateUntil();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.dateUntil), false,
+ "isConstructor(Temporal.Calendar.prototype.dateUntil)");
diff --git a/test/built-ins/Temporal/Calendar/prototype/dateUntil/prop-desc.js b/test/built-ins/Temporal/Calendar/prototype/dateUntil/prop-desc.js
new file mode 100644
index 00000000000..b0ce5761b97
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dateUntil/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateuntil
+description: The "dateUntil" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.dateUntil,
+ "function",
+ "`typeof Calendar.prototype.dateUntil` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "dateUntil", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..4aba77d4c46
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.day
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.day(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..cd75839b016
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.day
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.day(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..6f26cfb828d
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/day/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.day
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.day(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/day/basic.js b/test/built-ins/Temporal/Calendar/prototype/day/basic.js
new file mode 100644
index 00000000000..3ae7fd4421a
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/day/basic.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.day
+description: Basic tests for day().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = 5;
+assert.sameValue(iso.day(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.day(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.day(Temporal.PlainMonthDay.from("11-05")), res, "PlainMonthDay");
+assert.sameValue(iso.day({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.day("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.day({ year: 2000 }), "property bag with missing properties");
diff --git a/test/built-ins/Temporal/Calendar/prototype/day/builtin.js b/test/built-ins/Temporal/Calendar/prototype/day/builtin.js
new file mode 100644
index 00000000000..cb44767482d
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/day/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.day
+description: >
+ Tests that Temporal.Calendar.prototype.day
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.day),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.day),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.day),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.day.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/day/calendar-fields-iterable.js b/test/built-ins/Temporal/Calendar/prototype/day/calendar-fields-iterable.js
new file mode 100644
index 00000000000..55a1eb8ebe1
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/day/calendar-fields-iterable.js
@@ -0,0 +1,34 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.day
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.day step 4:
+ 4. Return ? ISODay(_dateOrDateTime_).
+ sec-temporal-isoday step 1.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(calendar, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.day({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/Calendar/prototype/day/calendar-temporal-object.js b/test/built-ins/Temporal/Calendar/prototype/day/calendar-temporal-object.js
new file mode 100644
index 00000000000..ce9beec1838
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/day/calendar-temporal-object.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.day
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.day step 4:
+ 4. Return ? ISODay(_dateOrDateTime_).
+ sec-temporal-isoday step 1.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.day({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/day/infinity-throws-rangeerror.js b/test/built-ins/Temporal/Calendar/prototype/day/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..0299adbd58e
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/day/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.day
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.day({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.day({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/day/length.js b/test/built-ins/Temporal/Calendar/prototype/day/length.js
new file mode 100644
index 00000000000..df2b2e82ac8
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/day/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.day
+description: Temporal.Calendar.prototype.day.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.day, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/day/name.js b/test/built-ins/Temporal/Calendar/prototype/day/name.js
new file mode 100644
index 00000000000..260ecb88760
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/day/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.day
+description: Temporal.Calendar.prototype.day.name is "day".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.day, "name", {
+ value: "day",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/day/not-a-constructor.js b/test/built-ins/Temporal/Calendar/prototype/day/not-a-constructor.js
new file mode 100644
index 00000000000..fcb976cd0c3
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/day/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.day
+description: >
+ Temporal.Calendar.prototype.day does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.day();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.day), false,
+ "isConstructor(Temporal.Calendar.prototype.day)");
diff --git a/test/built-ins/Temporal/Calendar/prototype/day/prop-desc.js b/test/built-ins/Temporal/Calendar/prototype/day/prop-desc.js
new file mode 100644
index 00000000000..dd590a2245d
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/day/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.day
+description: The "day" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.day,
+ "function",
+ "`typeof Calendar.prototype.day` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "day", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..3730b6d373f
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofweek
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.dayOfWeek(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..13bb558cf67
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofweek
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.dayOfWeek(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..dd21b6c28bb
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofweek
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.dayOfWeek(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/basic.js b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/basic.js
new file mode 100644
index 00000000000..f8d6b84ae50
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/basic.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofweek
+description: Basic tests for dayOfWeek().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = 6;
+assert.sameValue(iso.dayOfWeek(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.dayOfWeek(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.dayOfWeek({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.dayOfWeek("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.dayOfWeek({ year: 2000 }), "property bag with missing properties");
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/builtin.js b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/builtin.js
new file mode 100644
index 00000000000..85957bda82d
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofweek
+description: >
+ Tests that Temporal.Calendar.prototype.dayOfWeek
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.dayOfWeek),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.dayOfWeek),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.dayOfWeek),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.dayOfWeek.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/calendar-fields-iterable.js b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/calendar-fields-iterable.js
new file mode 100644
index 00000000000..3f8a861f64c
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/calendar-fields-iterable.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofweek
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.dayofweek step 4:
+ 4. Let _date_ be ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.dayOfWeek({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/calendar-temporal-object.js b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/calendar-temporal-object.js
new file mode 100644
index 00000000000..bdc9e50e2c4
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofweek
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.dayofweek step 4:
+ 4. Let _date_ be ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.dayOfWeek({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/infinity-throws-rangeerror.js b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..ad7c225f87e
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.dayofweek
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.dayOfWeek({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.dayOfWeek({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/length.js b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/length.js
new file mode 100644
index 00000000000..0192cbf9157
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofweek
+description: Temporal.Calendar.prototype.dayOfWeek.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.dayOfWeek, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/name.js b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/name.js
new file mode 100644
index 00000000000..ea8c9f97943
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofweek
+description: Temporal.Calendar.prototype.dayOfWeek.name is "dayOfWeek".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.dayOfWeek, "name", {
+ value: "dayOfWeek",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/not-a-constructor.js b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/not-a-constructor.js
new file mode 100644
index 00000000000..b5a3810f4be
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofweek
+description: >
+ Temporal.Calendar.prototype.dayOfWeek does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.dayOfWeek();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.dayOfWeek), false,
+ "isConstructor(Temporal.Calendar.prototype.dayOfWeek)");
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/prop-desc.js b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/prop-desc.js
new file mode 100644
index 00000000000..8f8f31290a0
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfWeek/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofweek
+description: The "dayOfWeek" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.dayOfWeek,
+ "function",
+ "`typeof Calendar.prototype.dayOfWeek` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "dayOfWeek", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..086fe1d5530
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofyear
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.dayOfYear(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..0bfff01396b
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofyear
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.dayOfYear(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..ec98fd7ede7
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofyear
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.dayOfYear(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfYear/basic.js b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/basic.js
new file mode 100644
index 00000000000..6c1621547a7
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/basic.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofyear
+description: Basic tests for dayOfYear().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = 309;
+assert.sameValue(iso.dayOfYear(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.dayOfYear(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.dayOfYear({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.dayOfYear("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.dayOfYear({ year: 2000 }), "property bag with missing properties");
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfYear/builtin.js b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/builtin.js
new file mode 100644
index 00000000000..8abab318b23
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofyear
+description: >
+ Tests that Temporal.Calendar.prototype.dayOfYear
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.dayOfYear),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.dayOfYear),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.dayOfYear),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.dayOfYear.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfYear/calendar-fields-iterable.js b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/calendar-fields-iterable.js
new file mode 100644
index 00000000000..902ec1f349d
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/calendar-fields-iterable.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofyear
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.dayofyear step 4:
+ 4. Let _date_ be ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.dayOfYear({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfYear/calendar-temporal-object.js b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/calendar-temporal-object.js
new file mode 100644
index 00000000000..414c9ac960f
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofyear
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.dayofyear step 4:
+ 4. Let _date_ be ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.dayOfYear({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfYear/infinity-throws-rangeerror.js b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..50a59ef1567
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.dayofyear
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.dayOfYear({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.dayOfYear({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfYear/length.js b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/length.js
new file mode 100644
index 00000000000..045b87e800e
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofyear
+description: Temporal.Calendar.prototype.dayOfYear.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.dayOfYear, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfYear/name.js b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/name.js
new file mode 100644
index 00000000000..60dfa0b8e9a
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofyear
+description: Temporal.Calendar.prototype.dayOfYear.name is "dayOfYear".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.dayOfYear, "name", {
+ value: "dayOfYear",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfYear/not-a-constructor.js b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/not-a-constructor.js
new file mode 100644
index 00000000000..c66309a410f
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofyear
+description: >
+ Temporal.Calendar.prototype.dayOfYear does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.dayOfYear();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.dayOfYear), false,
+ "isConstructor(Temporal.Calendar.prototype.dayOfYear)");
diff --git a/test/built-ins/Temporal/Calendar/prototype/dayOfYear/prop-desc.js b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/prop-desc.js
new file mode 100644
index 00000000000..6d9c5be9123
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/dayOfYear/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofyear
+description: The "dayOfYear" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.dayOfYear,
+ "function",
+ "`typeof Calendar.prototype.dayOfYear` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "dayOfYear", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..9d38028a54a
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinmonth
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.daysInMonth(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..d80b2222ae9
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinmonth
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.daysInMonth(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..73b153938d4
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinmonth
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.daysInMonth(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInMonth/basic.js b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/basic.js
new file mode 100644
index 00000000000..7405e97587a
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/basic.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinmonth
+description: Basic tests for daysInMonth().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = 30;
+assert.sameValue(iso.daysInMonth(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.daysInMonth(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.daysInMonth({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.daysInMonth("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.daysInMonth({ year: 2000 }), "property bag with missing properties");
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInMonth/builtin.js b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/builtin.js
new file mode 100644
index 00000000000..d588deb0aab
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinmonth
+description: >
+ Tests that Temporal.Calendar.prototype.daysInMonth
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.daysInMonth),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.daysInMonth),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.daysInMonth),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.daysInMonth.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInMonth/calendar-fields-iterable.js b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/calendar-fields-iterable.js
new file mode 100644
index 00000000000..835fc7bea21
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/calendar-fields-iterable.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinmonth
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.daysinmonth step 4.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.daysInMonth({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInMonth/calendar-temporal-object.js b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/calendar-temporal-object.js
new file mode 100644
index 00000000000..a2aa62f9e37
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinmonth
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.daysinmonth step 4.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.daysInMonth({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInMonth/infinity-throws-rangeerror.js b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..83a50e18cbc
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.daysinmonth
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.daysInMonth({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.daysInMonth({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInMonth/length.js b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/length.js
new file mode 100644
index 00000000000..ca315427f3e
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinmonth
+description: Temporal.Calendar.prototype.daysInMonth.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.daysInMonth, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInMonth/name.js b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/name.js
new file mode 100644
index 00000000000..8634c31f4bd
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinmonth
+description: Temporal.Calendar.prototype.daysInMonth.name is "daysInMonth".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.daysInMonth, "name", {
+ value: "daysInMonth",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInMonth/not-a-constructor.js b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/not-a-constructor.js
new file mode 100644
index 00000000000..d621f79b0b6
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinmonth
+description: >
+ Temporal.Calendar.prototype.daysInMonth does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.daysInMonth();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.daysInMonth), false,
+ "isConstructor(Temporal.Calendar.prototype.daysInMonth)");
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInMonth/prop-desc.js b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/prop-desc.js
new file mode 100644
index 00000000000..5ea6170c686
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInMonth/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinmonth
+description: The "daysInMonth" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.daysInMonth,
+ "function",
+ "`typeof Calendar.prototype.daysInMonth` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "daysInMonth", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..03c9ed64332
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinweek
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.daysInWeek(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..e7cf0a27334
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinweek
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.daysInWeek(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..b9c8693588a
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinweek
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.daysInWeek(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInWeek/basic.js b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/basic.js
new file mode 100644
index 00000000000..4392bc53224
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/basic.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinweek
+description: Basic tests for daysInWeek().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = 7;
+assert.sameValue(iso.daysInWeek(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.daysInWeek(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.daysInWeek({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.daysInWeek("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.daysInWeek({ year: 2000 }), "property bag with missing properties");
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInWeek/builtin.js b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/builtin.js
new file mode 100644
index 00000000000..e7781c447d7
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinweek
+description: >
+ Tests that Temporal.Calendar.prototype.daysInWeek
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.daysInWeek),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.daysInWeek),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.daysInWeek),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.daysInWeek.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInWeek/calendar-fields-iterable.js b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/calendar-fields-iterable.js
new file mode 100644
index 00000000000..fdc68aee0d1
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/calendar-fields-iterable.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinweek
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.daysinweek step 4:
+ 4. Perform ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.daysInWeek({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInWeek/calendar-temporal-object.js b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/calendar-temporal-object.js
new file mode 100644
index 00000000000..2cf851b1ff5
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinweek
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.daysinweek step 4:
+ 4. Perform ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.daysInWeek({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInWeek/infinity-throws-rangeerror.js b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..2e44133cdc3
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.daysinweek
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.daysInWeek({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.daysInWeek({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInWeek/length.js b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/length.js
new file mode 100644
index 00000000000..5cdbe2226e7
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinweek
+description: Temporal.Calendar.prototype.daysInWeek.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.daysInWeek, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInWeek/name.js b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/name.js
new file mode 100644
index 00000000000..06e99bcb85c
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinweek
+description: Temporal.Calendar.prototype.daysInWeek.name is "daysInWeek".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.daysInWeek, "name", {
+ value: "daysInWeek",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInWeek/not-a-constructor.js b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/not-a-constructor.js
new file mode 100644
index 00000000000..0d37c1cfc7a
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinweek
+description: >
+ Temporal.Calendar.prototype.daysInWeek does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.daysInWeek();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.daysInWeek), false,
+ "isConstructor(Temporal.Calendar.prototype.daysInWeek)");
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInWeek/prop-desc.js b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/prop-desc.js
new file mode 100644
index 00000000000..0b597f93d86
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInWeek/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinweek
+description: The "daysInWeek" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.daysInWeek,
+ "function",
+ "`typeof Calendar.prototype.daysInWeek` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "daysInWeek", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..8c9b14ba36b
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinyear
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.daysInYear(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..5fb548bddf1
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinyear
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.daysInYear(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..8c8f3ad8671
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinyear
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.daysInYear(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInYear/basic.js b/test/built-ins/Temporal/Calendar/prototype/daysInYear/basic.js
new file mode 100644
index 00000000000..37f70cd3ee8
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInYear/basic.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinyear
+description: Basic tests for daysInYear().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = 365;
+assert.sameValue(iso.daysInYear(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.daysInYear(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.daysInYear(Temporal.PlainYearMonth.from("1994-11")), res, "PlainYearMonth");
+assert.sameValue(iso.daysInYear({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.daysInYear("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.daysInYear({ year: 2000 }), "property bag with missing properties");
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInYear/builtin.js b/test/built-ins/Temporal/Calendar/prototype/daysInYear/builtin.js
new file mode 100644
index 00000000000..70eb5b2051a
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInYear/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinyear
+description: >
+ Tests that Temporal.Calendar.prototype.daysInYear
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.daysInYear),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.daysInYear),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.daysInYear),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.daysInYear.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInYear/calendar-fields-iterable.js b/test/built-ins/Temporal/Calendar/prototype/daysInYear/calendar-fields-iterable.js
new file mode 100644
index 00000000000..74f9874efa3
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInYear/calendar-fields-iterable.js
@@ -0,0 +1,34 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinyear
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.daysinyear step 4:
+ 4. Let _year_ be ? ISOYear(_dateOrDateTime_).
+ sec-temporal-isoyear step 1.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.daysInYear({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInYear/calendar-temporal-object.js b/test/built-ins/Temporal/Calendar/prototype/daysInYear/calendar-temporal-object.js
new file mode 100644
index 00000000000..0334233bfb6
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInYear/calendar-temporal-object.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinyear
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.daysinyear step 4:
+ 4. Let _year_ be ? ISOYear(_dateOrDateTime_).
+ sec-temporal-isoyear step 1.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.daysInYear({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInYear/infinity-throws-rangeerror.js b/test/built-ins/Temporal/Calendar/prototype/daysInYear/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..8c21c5b7a75
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInYear/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.daysinyear
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.daysInYear({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.daysInYear({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInYear/length.js b/test/built-ins/Temporal/Calendar/prototype/daysInYear/length.js
new file mode 100644
index 00000000000..52deaf36d8b
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInYear/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinyear
+description: Temporal.Calendar.prototype.daysInYear.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.daysInYear, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInYear/name.js b/test/built-ins/Temporal/Calendar/prototype/daysInYear/name.js
new file mode 100644
index 00000000000..1adacb830ba
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInYear/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinyear
+description: Temporal.Calendar.prototype.daysInYear.name is "daysInYear".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.daysInYear, "name", {
+ value: "daysInYear",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInYear/not-a-constructor.js b/test/built-ins/Temporal/Calendar/prototype/daysInYear/not-a-constructor.js
new file mode 100644
index 00000000000..c351d1e5acd
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInYear/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinyear
+description: >
+ Temporal.Calendar.prototype.daysInYear does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.daysInYear();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.daysInYear), false,
+ "isConstructor(Temporal.Calendar.prototype.daysInYear)");
diff --git a/test/built-ins/Temporal/Calendar/prototype/daysInYear/prop-desc.js b/test/built-ins/Temporal/Calendar/prototype/daysInYear/prop-desc.js
new file mode 100644
index 00000000000..0f2af09c078
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/daysInYear/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.daysinyear
+description: The "daysInYear" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.daysInYear,
+ "function",
+ "`typeof Calendar.prototype.daysInYear` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "daysInYear", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/fields/argument-iterable-not-array.js b/test/built-ins/Temporal/Calendar/prototype/fields/argument-iterable-not-array.js
new file mode 100644
index 00000000000..31e6a75b758
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/fields/argument-iterable-not-array.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.fields
+description: A non-Array iterable passed as the argument is exhausted
+info: |
+ sec-temporal.calendar.prototype.fields step 4:
+ 4. Let _fieldNames_ be ? IterableToList(_fields_).
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const fieldNames = ["day", "month", "monthCode", "year"];
+const iterable = {
+ iteratorExhausted: false,
+ *[Symbol.iterator]() {
+ yield* fieldNames;
+ this.iteratorExhausted = true;
+ },
+};
+
+const calendar = new Temporal.Calendar("iso8601");
+const result = calendar.fields(iterable);
+
+assert.compareArray(result, fieldNames);
+assert(iterable.iteratorExhausted);
diff --git a/test/built-ins/Temporal/Calendar/prototype/fields/argument-throws-duplicate-keys.js b/test/built-ins/Temporal/Calendar/prototype/fields/argument-throws-duplicate-keys.js
new file mode 100644
index 00000000000..f34d85f7ea3
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/fields/argument-throws-duplicate-keys.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.fields
+description: Duplicate fields are not allowed in the argument to Calendar.prototype.fields
+info: |
+ sec-temporal.calendar.prototype.fields step 7.b.iii:
+ iii. If _fieldNames_ contains _nextValue_, then
+ 1. Let _completion_ be ThrowCompletion(a newly created *RangeError* object).
+ 2. Return ? IteratorClose(_iteratorRecord_, _completion_).
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+assert.throws(RangeError, () => calendar.fields(["day", "month", "day"]));
+assert.throws(RangeError, () => calendar.fields(["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "year"]));
+
+const manyFields = {
+ count: 0
+};
+
+manyFields[Symbol.iterator] = function*() {
+ while (this.count++ < 100) yield "year";
+};
+
+assert.throws(RangeError, () => calendar.fields(manyFields), "Rejected duplicate values");
+assert.sameValue(manyFields.count, 2, "Rejected first duplicate value");
diff --git a/test/built-ins/Temporal/Calendar/prototype/fields/argument-throws-invalid-keys.js b/test/built-ins/Temporal/Calendar/prototype/fields/argument-throws-invalid-keys.js
new file mode 100644
index 00000000000..ec6a73b6932
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/fields/argument-throws-invalid-keys.js
@@ -0,0 +1,35 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.fields
+description: Calendar.prototype.fields rejects input field names that are not singular names of temporal units
+info: |
+ sec-temporal.calendar.prototype.fields step 7.b.ii:
+ 7. Repeat, while next is not false,
+ a. Set next to ? IteratorStep(iteratorRecord).
+ b. If next is not false, then
+ i. Let nextValue be ? IteratorValue(next).
+ ii. If Type(nextValue) is not String, then
+ 1. Let completion be ThrowCompletion(a newly created TypeError object).
+ 2. Return ? IteratorClose(iteratorRecord, completion).
+ sec-temporal.calendar.prototype.fields step 7.b.iv:
+ iv. If _nextValue_ is not one of *"year"*, *"month"*, *"monthCode"*, *"day"*, *"hour"*, *"minute"*, *"second"*, *"millisecond"*, *"microsecond"*, *"nanosecond"*, then
+ 1. Let _completion_ be ThrowCompletion(a newly created *RangeError* object).
+ 2. Return ? IteratorClose(_iteratorRecord_, _completion_).
+
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+assert.throws(TypeError, () => calendar.fields([1]));
+assert.throws(TypeError, () => calendar.fields([1n]));
+assert.throws(TypeError, () => calendar.fields([Symbol('foo')]));
+assert.throws(TypeError, () => calendar.fields([true]));
+assert.throws(TypeError, () => calendar.fields([null]));
+assert.throws(TypeError, () => calendar.fields([{}]));
+assert.throws(TypeError, () => calendar.fields([undefined]));
+assert.throws(TypeError, () => calendar.fields(["day", 1]));
+assert.throws(RangeError, () => calendar.fields(["month", "days"]));
+assert.throws(RangeError, () => calendar.fields(["days"]));
+assert.throws(RangeError, () => calendar.fields(["people"]));
diff --git a/test/built-ins/Temporal/Calendar/prototype/fields/builtin.js b/test/built-ins/Temporal/Calendar/prototype/fields/builtin.js
new file mode 100644
index 00000000000..7467b415cec
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/fields/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.fields
+description: >
+ Tests that Temporal.Calendar.prototype.fields
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.fields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.fields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.fields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.fields.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/fields/length.js b/test/built-ins/Temporal/Calendar/prototype/fields/length.js
new file mode 100644
index 00000000000..b5d53e02103
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/fields/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.fields
+description: Temporal.Calendar.prototype.fields.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.fields, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/fields/name.js b/test/built-ins/Temporal/Calendar/prototype/fields/name.js
new file mode 100644
index 00000000000..1d80b465503
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/fields/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.fields
+description: Temporal.Calendar.prototype.fields.name is "fields".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.fields, "name", {
+ value: "fields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/fields/non-string-element-throws.js b/test/built-ins/Temporal/Calendar/prototype/fields/non-string-element-throws.js
new file mode 100644
index 00000000000..40ca483693f
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/fields/non-string-element-throws.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.fields
+description: TypeError thrown if the input iterable yields a non-String value
+info: |
+ sec-temporal.calendar.prototype.fields step 5:
+ 5. For each element _fieldName_ of _fieldNames_, do
+ a. If Type(_fieldName_) is not String, throw a *TypeError* exception.
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+[true, 3, 3n, {}, () => {}, Symbol(), undefined, null].forEach((element) => {
+ assert.throws(TypeError, () => calendar.fields([element]), "bad input to calendar.fields()");
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/fields/not-a-constructor.js b/test/built-ins/Temporal/Calendar/prototype/fields/not-a-constructor.js
new file mode 100644
index 00000000000..d38455b080a
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/fields/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.fields
+description: >
+ Temporal.Calendar.prototype.fields does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.fields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.fields), false,
+ "isConstructor(Temporal.Calendar.prototype.fields)");
diff --git a/test/built-ins/Temporal/Calendar/prototype/fields/prop-desc.js b/test/built-ins/Temporal/Calendar/prototype/fields/prop-desc.js
new file mode 100644
index 00000000000..480de4f9cf9
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/fields/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.fields
+description: The "fields" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.fields,
+ "function",
+ "`typeof Calendar.prototype.fields` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "fields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/id/prop-desc.js b/test/built-ins/Temporal/Calendar/prototype/id/prop-desc.js
new file mode 100644
index 00000000000..7b2dbe4e914
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/id/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.calendar.prototype.id
+description: The "id" property of Temporal.Calendar.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..4899fee9201
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.inleapyear
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.inLeapYear(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..3122822de53
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.inleapyear
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.inLeapYear(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..6b114ea0f82
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.inleapyear
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.inLeapYear(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/inLeapYear/basic.js b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/basic.js
new file mode 100644
index 00000000000..4359c4c8d44
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/basic.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.inleapyear
+description: Basic tests for inLeapYear().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = false;
+assert.sameValue(iso.inLeapYear(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.inLeapYear(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.inLeapYear(Temporal.PlainYearMonth.from("1994-11")), res, "PlainYearMonth");
+assert.sameValue(iso.inLeapYear({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.inLeapYear("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.inLeapYear({ year: 2000 }), "property bag with missing properties");
diff --git a/test/built-ins/Temporal/Calendar/prototype/inLeapYear/builtin.js b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/builtin.js
new file mode 100644
index 00000000000..2c66b6b67a0
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.inleapyear
+description: >
+ Tests that Temporal.Calendar.prototype.inLeapYear
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.inLeapYear),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.inLeapYear),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.inLeapYear),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.inLeapYear.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/inLeapYear/calendar-fields-iterable.js b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/calendar-fields-iterable.js
new file mode 100644
index 00000000000..26719069f3a
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/calendar-fields-iterable.js
@@ -0,0 +1,34 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.inleapyear
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.inleapyear step 4:
+ 4. Let _year_ be ? ISOYear(_dateOrDateTime_).
+ sec-temporal-isoyear step 1.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.inLeapYear({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/Calendar/prototype/inLeapYear/calendar-temporal-object.js b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/calendar-temporal-object.js
new file mode 100644
index 00000000000..e31a15dd393
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.inleapyear
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.inleapyear step 4:
+ 4. Let _year_ be ? ISOYear(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.inLeapYear({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/inLeapYear/infinity-throws-rangeerror.js b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..083bbb50735
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.inleapyear
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.inLeapYear({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.inLeapYear({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/inLeapYear/length.js b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/length.js
new file mode 100644
index 00000000000..b186375f710
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.inleapyear
+description: Temporal.Calendar.prototype.inLeapYear.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.inLeapYear, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/inLeapYear/name.js b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/name.js
new file mode 100644
index 00000000000..e5efe9b8375
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.inleapyear
+description: Temporal.Calendar.prototype.inLeapYear.name is "inLeapYear".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.inLeapYear, "name", {
+ value: "inLeapYear",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/inLeapYear/not-a-constructor.js b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/not-a-constructor.js
new file mode 100644
index 00000000000..fcafccbbef2
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.inleapyear
+description: >
+ Temporal.Calendar.prototype.inLeapYear does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.inLeapYear();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.inLeapYear), false,
+ "isConstructor(Temporal.Calendar.prototype.inLeapYear)");
diff --git a/test/built-ins/Temporal/Calendar/prototype/inLeapYear/prop-desc.js b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/prop-desc.js
new file mode 100644
index 00000000000..eb21f1a0497
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/inLeapYear/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.inleapyear
+description: The "inLeapYear" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.inLeapYear,
+ "function",
+ "`typeof Calendar.prototype.inLeapYear` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "inLeapYear", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/mergeFields/arguments-empty-object.js b/test/built-ins/Temporal/Calendar/prototype/mergeFields/arguments-empty-object.js
new file mode 100644
index 00000000000..bd8d760257e
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/mergeFields/arguments-empty-object.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.mergefields
+description: Either argument being an empty object should result in a copy of the other object
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+
+let calls = 0;
+const yearObserver = {
+ get year() {
+ calls++;
+ return 2021;
+ }
+};
+
+const result1 = calendar.mergeFields(yearObserver, {});
+assert.sameValue(calls, 1, "property copied");
+assert.compareArray(Object.keys(result1), ["year"]);
+assert.sameValue(result1.year, 2021);
+assert.sameValue(calls, 1, "result has a data property");
+
+calls = 0;
+const result2 = calendar.mergeFields({}, yearObserver);
+assert.sameValue(calls, 1, "property copied");
+assert.compareArray(Object.keys(result2), ["year"]);
+assert.sameValue(result2.year, 2021);
+assert.sameValue(calls, 1, "result has a data property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/mergeFields/arguments-not-object.js b/test/built-ins/Temporal/Calendar/prototype/mergeFields/arguments-not-object.js
new file mode 100644
index 00000000000..b9e40fe5c5d
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/mergeFields/arguments-not-object.js
@@ -0,0 +1,40 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.mergefields
+description: Non-object arguments are converted with ToObject and merge their [[OwnPropertyKeys]]
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+
+assert.throws(TypeError, () => calendar.mergeFields(undefined, {}));
+assert.throws(TypeError, () => calendar.mergeFields({}, undefined));
+
+assert.throws(TypeError, () => calendar.mergeFields(null, {}));
+assert.throws(TypeError, () => calendar.mergeFields({}, null));
+
+const boolResult = calendar.mergeFields(true, false);
+assert.compareArray(Object.keys(boolResult), [], "Boolean objects have no own property keys");
+assert.sameValue(Object.getPrototypeOf(boolResult), Object.prototype, "plain object returned");
+
+const numResult = calendar.mergeFields(3, 4);
+assert.compareArray(Object.keys(numResult), [], "Number objects have no own property keys");
+assert.sameValue(Object.getPrototypeOf(boolResult), Object.prototype, "plain object returned");
+
+const strResult = calendar.mergeFields("abc", "de");
+assert.compareArray(Object.keys(strResult), ["0", "1", "2"], "String objects have integer indices as own property keys");
+assert.sameValue(strResult["0"], "d");
+assert.sameValue(strResult["1"], "e");
+assert.sameValue(strResult["2"], "c");
+assert.sameValue(Object.getPrototypeOf(boolResult), Object.prototype, "plain object returned");
+
+const symResult = calendar.mergeFields(Symbol("foo"), Symbol("bar"));
+assert.compareArray(Object.keys(symResult), [], "Symbol objects have no own property keys");
+assert.sameValue(Object.getPrototypeOf(symResult), Object.prototype, "plain object returned");
+
+const bigintResult = calendar.mergeFields(3n, 4n);
+assert.compareArray(Object.keys(bigintResult), [], "BigInt objects have no own property keys");
+assert.sameValue(Object.getPrototypeOf(bigintResult), Object.prototype, "plain object returned");
diff --git a/test/built-ins/Temporal/Calendar/prototype/mergeFields/builtin.js b/test/built-ins/Temporal/Calendar/prototype/mergeFields/builtin.js
new file mode 100644
index 00000000000..721c566e000
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/mergeFields/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.mergefields
+description: >
+ Tests that Temporal.Calendar.prototype.mergeFields
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.mergeFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.mergeFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.mergeFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.mergeFields.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/mergeFields/length.js b/test/built-ins/Temporal/Calendar/prototype/mergeFields/length.js
new file mode 100644
index 00000000000..204a370fda2
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/mergeFields/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.mergefields
+description: Temporal.Calendar.prototype.mergeFields.length is 2
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.mergeFields, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/mergeFields/name.js b/test/built-ins/Temporal/Calendar/prototype/mergeFields/name.js
new file mode 100644
index 00000000000..ec7de2c03dd
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/mergeFields/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.mergefields
+description: Temporal.Calendar.prototype.mergeFields.name is "mergeFields".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.mergeFields, "name", {
+ value: "mergeFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/mergeFields/not-a-constructor.js b/test/built-ins/Temporal/Calendar/prototype/mergeFields/not-a-constructor.js
new file mode 100644
index 00000000000..1e4519325b7
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/mergeFields/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.mergefields
+description: >
+ Temporal.Calendar.prototype.mergeFields does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.mergeFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.mergeFields), false,
+ "isConstructor(Temporal.Calendar.prototype.mergeFields)");
diff --git a/test/built-ins/Temporal/Calendar/prototype/mergeFields/prop-desc.js b/test/built-ins/Temporal/Calendar/prototype/mergeFields/prop-desc.js
new file mode 100644
index 00000000000..b5363f63f0a
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/mergeFields/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.mergefields
+description: The "mergeFields" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.mergeFields,
+ "function",
+ "`typeof Calendar.prototype.mergeFields` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "mergeFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..689ee484cf2
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.month
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.month(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..2275d1664c5
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.month
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.month(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..9e7b63a33c4
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/month/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.month
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.month(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/month/basic.js b/test/built-ins/Temporal/Calendar/prototype/month/basic.js
new file mode 100644
index 00000000000..17eb081e592
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/month/basic.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.month
+description: Basic tests for month().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = 11;
+assert.sameValue(iso.month(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.month(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.month(Temporal.PlainYearMonth.from("1994-11")), res, "PlainYearMonth");
+assert.sameValue(iso.month({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.month("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.month({ year: 2000 }), "property bag with missing properties");
+assert.throws(TypeError, () => iso.month(Temporal.PlainMonthDay.from("11-05")), "PlainMonthDay");
diff --git a/test/built-ins/Temporal/Calendar/prototype/month/builtin.js b/test/built-ins/Temporal/Calendar/prototype/month/builtin.js
new file mode 100644
index 00000000000..e2445cc42f0
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/month/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.month
+description: >
+ Tests that Temporal.Calendar.prototype.month
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.month),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.month),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.month),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.month.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/month/calendar-fields-iterable.js b/test/built-ins/Temporal/Calendar/prototype/month/calendar-fields-iterable.js
new file mode 100644
index 00000000000..ed9939f5c7b
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/month/calendar-fields-iterable.js
@@ -0,0 +1,34 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.month
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.month step 4:
+ 4. Return ? ISOMonth(_dateOrDateTime_).
+ sec-temporal-isomonth step 1.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.month({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/Calendar/prototype/month/calendar-temporal-object.js b/test/built-ins/Temporal/Calendar/prototype/month/calendar-temporal-object.js
new file mode 100644
index 00000000000..907cd78aff0
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/month/calendar-temporal-object.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.month
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.month step 4:
+ 4. Return ? ISOMonth(_dateOrDateTime_).
+ sec-temporal-isomonth step 1.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.month({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/month/infinity-throws-rangeerror.js b/test/built-ins/Temporal/Calendar/prototype/month/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..2bfe2315e31
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/month/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.month
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.month({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.month({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/month/length.js b/test/built-ins/Temporal/Calendar/prototype/month/length.js
new file mode 100644
index 00000000000..52af40a939d
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/month/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.month
+description: Temporal.Calendar.prototype.month.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.month, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/month/name.js b/test/built-ins/Temporal/Calendar/prototype/month/name.js
new file mode 100644
index 00000000000..79dc0b606ae
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/month/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.month
+description: Temporal.Calendar.prototype.month.name is "month".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.month, "name", {
+ value: "month",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/month/not-a-constructor.js b/test/built-ins/Temporal/Calendar/prototype/month/not-a-constructor.js
new file mode 100644
index 00000000000..31676f121a2
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/month/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.month
+description: >
+ Temporal.Calendar.prototype.month does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.month();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.month), false,
+ "isConstructor(Temporal.Calendar.prototype.month)");
diff --git a/test/built-ins/Temporal/Calendar/prototype/month/prop-desc.js b/test/built-ins/Temporal/Calendar/prototype/month/prop-desc.js
new file mode 100644
index 00000000000..684d4357005
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/month/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.month
+description: The "month" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.month,
+ "function",
+ "`typeof Calendar.prototype.month` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "month", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..9bd79d76e7e
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthcode
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.monthCode(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..708e07ec1b8
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthcode
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.monthCode(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..3eea738fd44
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthCode/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthcode
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.monthCode(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthCode/basic.js b/test/built-ins/Temporal/Calendar/prototype/monthCode/basic.js
new file mode 100644
index 00000000000..db6dcd1a740
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthCode/basic.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthcode
+description: Basic tests for monthCode().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = "M11";
+assert.sameValue(iso.monthCode(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.monthCode(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.monthCode(Temporal.PlainYearMonth.from("1994-11")), res, "PlainYearMonth");
+assert.sameValue(iso.monthCode(Temporal.PlainMonthDay.from("11-05")), res, "PlainMonthDay");
+assert.sameValue(iso.monthCode({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.monthCode("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.monthCode({ year: 2000 }), "property bag with missing properties");
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthCode/builtin.js b/test/built-ins/Temporal/Calendar/prototype/monthCode/builtin.js
new file mode 100644
index 00000000000..5f7dc2c95a3
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthCode/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthcode
+description: >
+ Tests that Temporal.Calendar.prototype.monthCode
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.monthCode),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.monthCode),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.monthCode),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.monthCode.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthCode/calendar-fields-iterable.js b/test/built-ins/Temporal/Calendar/prototype/monthCode/calendar-fields-iterable.js
new file mode 100644
index 00000000000..47a1dda723f
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthCode/calendar-fields-iterable.js
@@ -0,0 +1,34 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthcode
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.monthcode step 4:
+ 4. Return ? ISOMonthCode(_dateOrDateTime_).
+ sec-temporal-isomonthcode step 1.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.monthCode({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthCode/calendar-temporal-object.js b/test/built-ins/Temporal/Calendar/prototype/monthCode/calendar-temporal-object.js
new file mode 100644
index 00000000000..87004fdae71
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthCode/calendar-temporal-object.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthcode
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.monthcode step 4:
+ 4. Return ? ISOMonthCode(_dateOrDateTime_).
+ sec-temporal-isomonthcode step 1.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.monthCode({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthCode/infinity-throws-rangeerror.js b/test/built-ins/Temporal/Calendar/prototype/monthCode/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..49f99671fb1
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthCode/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.monthcode
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.monthCode({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.monthCode({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthCode/length.js b/test/built-ins/Temporal/Calendar/prototype/monthCode/length.js
new file mode 100644
index 00000000000..abc02d7dd67
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthCode/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthcode
+description: Temporal.Calendar.prototype.monthCode.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.monthCode, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthCode/name.js b/test/built-ins/Temporal/Calendar/prototype/monthCode/name.js
new file mode 100644
index 00000000000..818c5d20d89
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthCode/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthcode
+description: Temporal.Calendar.prototype.monthCode.name is "monthCode".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.monthCode, "name", {
+ value: "monthCode",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthCode/not-a-constructor.js b/test/built-ins/Temporal/Calendar/prototype/monthCode/not-a-constructor.js
new file mode 100644
index 00000000000..78dccfe5b1c
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthCode/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthcode
+description: >
+ Temporal.Calendar.prototype.monthCode does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.monthCode();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.monthCode), false,
+ "isConstructor(Temporal.Calendar.prototype.monthCode)");
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthCode/prop-desc.js b/test/built-ins/Temporal/Calendar/prototype/monthCode/prop-desc.js
new file mode 100644
index 00000000000..3dca68e969e
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthCode/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthcode
+description: The "monthCode" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.monthCode,
+ "function",
+ "`typeof Calendar.prototype.monthCode` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "monthCode", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/builtin.js b/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/builtin.js
new file mode 100644
index 00000000000..d21652fc593
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthdayfromfields
+description: >
+ Tests that Temporal.Calendar.prototype.monthDayFromFields
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.monthDayFromFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.monthDayFromFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.monthDayFromFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.monthDayFromFields.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/fields-not-object.js b/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/fields-not-object.js
new file mode 100644
index 00000000000..f8400f71854
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/fields-not-object.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthdayfromfields
+description: Throw a TypeError if the fields is not an object
+features: [Symbol, Temporal]
+---*/
+
+const tests = [undefined, null, false, "string", Symbol("sym"), Math.PI, 42n];
+const iso = Temporal.Calendar.from("iso8601");
+for (const fields of tests) {
+ assert.throws(TypeError, () => iso.monthDayFromFields(fields, {}));
+}
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/infinity-throws-rangeerror.js b/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..66e2a03b8b8
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/infinity-throws-rangeerror.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.monthdayfromfields
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.monthDayFromFields({ ...base, [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.monthDayFromFields({ ...base, [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/length.js b/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/length.js
new file mode 100644
index 00000000000..a8049a487a3
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthdayfromfields
+description: Temporal.Calendar.prototype.monthDayFromFields.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.monthDayFromFields, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/name.js b/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/name.js
new file mode 100644
index 00000000000..c3803624825
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthdayfromfields
+description: Temporal.Calendar.prototype.monthDayFromFields.name is "monthDayFromFields".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.monthDayFromFields, "name", {
+ value: "monthDayFromFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/not-a-constructor.js b/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/not-a-constructor.js
new file mode 100644
index 00000000000..63bcff79eae
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthdayfromfields
+description: >
+ Temporal.Calendar.prototype.monthDayFromFields does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.monthDayFromFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.monthDayFromFields), false,
+ "isConstructor(Temporal.Calendar.prototype.monthDayFromFields)");
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-invalid-string.js b/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-invalid-string.js
new file mode 100644
index 00000000000..2e87dc1a67a
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-invalid-string.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthdayfromfields
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isomonthdayfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.calendar.prototype.monthdayfromfields step 6:
+ 6. Let _result_ be ? ISOMonthDayFromFields(_fields_, _options_).
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+assert.throws(RangeError, () => calendar.monthDayFromFields({ year: 2000, month: 5, day: 2 }, { overflow: "other string" }));
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-undefined.js b/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-undefined.js
new file mode 100644
index 00000000000..75479d317ae
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthdayfromfields
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isomonthdayfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.calendar.prototype.monthdayfromfields step 6:
+ 6. Let _result_ be ? ISOMonthDayFromFields(_fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+
+const explicit = calendar.monthDayFromFields({ year: 2000, month: 15, day: 2 }, { overflow: undefined });
+TemporalHelpers.assertPlainMonthDay(explicit, "M12", 2, "default overflow is constrain");
+const implicit = calendar.monthDayFromFields({ year: 2000, month: 15, day: 2 }, {});
+TemporalHelpers.assertPlainMonthDay(implicit, "M12", 2, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-wrong-type.js b/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-wrong-type.js
new file mode 100644
index 00000000000..197ad0677ea
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/overflow-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthdayfromfields
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isomonthdayfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.calendar.prototype.monthdayfromfields step 6:
+ 6. Let _result_ be ? ISOMonthDayFromFields(_fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => calendar.monthDayFromFields({ year: 2000, month: 5, day: 2 }, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainMonthDay(result, "M05", 2, descr),
+);
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/prop-desc.js b/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/prop-desc.js
new file mode 100644
index 00000000000..edfb6881fc6
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthDayFromFields/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthdayfromfields
+description: The "monthDayFromFields" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.monthDayFromFields,
+ "function",
+ "`typeof Calendar.prototype.monthDayFromFields` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "monthDayFromFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..f2d34fd60b2
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthsinyear
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.monthsInYear(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..30c4b99708f
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthsinyear
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.monthsInYear(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..b6ae9f5e509
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthsinyear
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.monthsInYear(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthsInYear/basic.js b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/basic.js
new file mode 100644
index 00000000000..fcf4e35084a
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/basic.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthsinyear
+description: Basic tests for monthsInYear().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = 12;
+assert.sameValue(iso.monthsInYear(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.monthsInYear(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.monthsInYear(Temporal.PlainYearMonth.from("1994-11")), res, "PlainYearMonth");
+assert.sameValue(iso.monthsInYear({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.monthsInYear("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.monthsInYear({ year: 2000 }), "property bag with missing properties");
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthsInYear/builtin.js b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/builtin.js
new file mode 100644
index 00000000000..bb572670642
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthsinyear
+description: >
+ Tests that Temporal.Calendar.prototype.monthsInYear
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.monthsInYear),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.monthsInYear),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.monthsInYear),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.monthsInYear.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthsInYear/calendar-fields-iterable.js b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/calendar-fields-iterable.js
new file mode 100644
index 00000000000..79b6eb0ac22
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/calendar-fields-iterable.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthsinyear
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.monthsinyear step 4:
+ 4. Perform ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.monthsInYear({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthsInYear/calendar-temporal-object.js b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/calendar-temporal-object.js
new file mode 100644
index 00000000000..3d6418662b0
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthsinyear
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.monthsinyear step 4:
+ 4. Perform ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.monthsInYear({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthsInYear/infinity-throws-rangeerror.js b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..eac600f4670
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.monthsinyear
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.monthsInYear({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.monthsInYear({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthsInYear/length.js b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/length.js
new file mode 100644
index 00000000000..ac6057351de
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthsinyear
+description: Temporal.Calendar.prototype.monthsInYear.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.monthsInYear, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthsInYear/name.js b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/name.js
new file mode 100644
index 00000000000..ce9468d16f3
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthsinyear
+description: Temporal.Calendar.prototype.monthsInYear.name is "monthsInYear".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.monthsInYear, "name", {
+ value: "monthsInYear",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthsInYear/not-a-constructor.js b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/not-a-constructor.js
new file mode 100644
index 00000000000..9e9a64d9646
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthsinyear
+description: >
+ Temporal.Calendar.prototype.monthsInYear does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.monthsInYear();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.monthsInYear), false,
+ "isConstructor(Temporal.Calendar.prototype.monthsInYear)");
diff --git a/test/built-ins/Temporal/Calendar/prototype/monthsInYear/prop-desc.js b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/prop-desc.js
new file mode 100644
index 00000000000..c6b47d059df
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/monthsInYear/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.monthsinyear
+description: The "monthsInYear" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.monthsInYear,
+ "function",
+ "`typeof Calendar.prototype.monthsInYear` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "monthsInYear", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/toJSON/builtin.js b/test/built-ins/Temporal/Calendar/prototype/toJSON/builtin.js
new file mode 100644
index 00000000000..f2ab05afa82
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/toJSON/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.tojson
+description: >
+ Tests that Temporal.Calendar.prototype.toJSON
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/toJSON/length.js b/test/built-ins/Temporal/Calendar/prototype/toJSON/length.js
new file mode 100644
index 00000000000..d616b4205f9
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/toJSON/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.tojson
+description: Temporal.Calendar.prototype.toJSON.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/toJSON/name.js b/test/built-ins/Temporal/Calendar/prototype/toJSON/name.js
new file mode 100644
index 00000000000..7ad399501c8
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/toJSON/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.tojson
+description: Temporal.Calendar.prototype.toJSON.name is "toJSON".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/toJSON/not-a-constructor.js b/test/built-ins/Temporal/Calendar/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 00000000000..b8b839f23b4
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/toJSON/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.tojson
+description: >
+ Temporal.Calendar.prototype.toJSON does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.toJSON), false,
+ "isConstructor(Temporal.Calendar.prototype.toJSON)");
diff --git a/test/built-ins/Temporal/Calendar/prototype/toJSON/prop-desc.js b/test/built-ins/Temporal/Calendar/prototype/toJSON/prop-desc.js
new file mode 100644
index 00000000000..e1b676ad6ad
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/toJSON/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.tojson
+description: The "toJSON" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.toJSON,
+ "function",
+ "`typeof Calendar.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/toString/builtin.js b/test/built-ins/Temporal/Calendar/prototype/toString/builtin.js
new file mode 100644
index 00000000000..d2a6ca26eed
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/toString/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.tostring
+description: >
+ Tests that Temporal.Calendar.prototype.toString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/toString/length.js b/test/built-ins/Temporal/Calendar/prototype/toString/length.js
new file mode 100644
index 00000000000..2e14f87b003
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/toString/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.tostring
+description: Temporal.Calendar.prototype.toString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/toString/name.js b/test/built-ins/Temporal/Calendar/prototype/toString/name.js
new file mode 100644
index 00000000000..86904315dbf
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/toString/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.tostring
+description: Temporal.Calendar.prototype.toString.name is "toString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/toString/not-a-constructor.js b/test/built-ins/Temporal/Calendar/prototype/toString/not-a-constructor.js
new file mode 100644
index 00000000000..04bb7e013f5
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/toString/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.tostring
+description: >
+ Temporal.Calendar.prototype.toString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.toString), false,
+ "isConstructor(Temporal.Calendar.prototype.toString)");
diff --git a/test/built-ins/Temporal/Calendar/prototype/toString/prop-desc.js b/test/built-ins/Temporal/Calendar/prototype/toString/prop-desc.js
new file mode 100644
index 00000000000..20f371881ea
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/toString/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.tostring
+description: The "toString" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.toString,
+ "function",
+ "`typeof Calendar.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..6e0a16ce7f3
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.weekofyear
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.weekOfYear(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..75b54dbd177
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.weekofyear
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.weekOfYear(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..37c24885ad1
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.weekofyear
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.weekOfYear(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/weekOfYear/basic.js b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/basic.js
new file mode 100644
index 00000000000..9dd388e959b
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/basic.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.weekofyear
+description: Basic tests for weekOfYear().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = 44;
+assert.sameValue(iso.weekOfYear(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.weekOfYear(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.weekOfYear({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.weekOfYear("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.weekOfYear({ year: 2000 }), "property bag with missing properties");
diff --git a/test/built-ins/Temporal/Calendar/prototype/weekOfYear/builtin.js b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/builtin.js
new file mode 100644
index 00000000000..8234ce5a752
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.weekofyear
+description: >
+ Tests that Temporal.Calendar.prototype.weekOfYear
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.weekOfYear),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.weekOfYear),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.weekOfYear),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.weekOfYear.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/weekOfYear/calendar-fields-iterable.js b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/calendar-fields-iterable.js
new file mode 100644
index 00000000000..2c485c5b0a9
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/calendar-fields-iterable.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.weekofyear
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.weekofyear step 4:
+ 4. Let _date_ be ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.weekOfYear({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/Calendar/prototype/weekOfYear/calendar-temporal-object.js b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/calendar-temporal-object.js
new file mode 100644
index 00000000000..8dbfb76c93b
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.weekofyear
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.weekofyear step 4:
+ 4. Let _date_ be ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.weekOfYear({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/weekOfYear/cross-year.js b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/cross-year.js
new file mode 100644
index 00000000000..609c076fb7c
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/cross-year.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.weekofyear
+description: weekOfYear() crossing year boundaries.
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+assert.sameValue(iso.weekOfYear(Temporal.PlainDate.from("2019-12-31")), 1, "week 1 from next year");
+assert.sameValue(iso.weekOfYear(Temporal.PlainDate.from("2021-01-01")), 53, "week 1 from next year");
diff --git a/test/built-ins/Temporal/Calendar/prototype/weekOfYear/infinity-throws-rangeerror.js b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..6a65cf5befd
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.weekofyear
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.weekOfYear({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.weekOfYear({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/weekOfYear/length.js b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/length.js
new file mode 100644
index 00000000000..f1f2b5f2c63
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.weekofyear
+description: Temporal.Calendar.prototype.weekOfYear.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.weekOfYear, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/weekOfYear/name.js b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/name.js
new file mode 100644
index 00000000000..f7828b8bcb8
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.weekofyear
+description: Temporal.Calendar.prototype.weekOfYear.name is "weekOfYear".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.weekOfYear, "name", {
+ value: "weekOfYear",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/weekOfYear/not-a-constructor.js b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/not-a-constructor.js
new file mode 100644
index 00000000000..3d1d944a4b8
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.weekofyear
+description: >
+ Temporal.Calendar.prototype.weekOfYear does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.weekOfYear();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.weekOfYear), false,
+ "isConstructor(Temporal.Calendar.prototype.weekOfYear)");
diff --git a/test/built-ins/Temporal/Calendar/prototype/weekOfYear/prop-desc.js b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/prop-desc.js
new file mode 100644
index 00000000000..c0c9c672151
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/weekOfYear/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.weekofyear
+description: The "weekOfYear" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.weekOfYear,
+ "function",
+ "`typeof Calendar.prototype.weekOfYear` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "weekOfYear", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..841eb842ac4
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.year
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.year(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..4c6b218ece1
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.year
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => calendar.year(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..69ff998d533
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/year/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.year
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => calendar.year(datetime));
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/year/basic.js b/test/built-ins/Temporal/Calendar/prototype/year/basic.js
new file mode 100644
index 00000000000..1d03e3cd77d
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/year/basic.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.year
+description: Basic tests for year().
+features: [Temporal]
+---*/
+
+const iso = Temporal.Calendar.from("iso8601");
+const res = 1994;
+assert.sameValue(iso.year(Temporal.PlainDate.from("1994-11-05")), res, "PlainDate");
+assert.sameValue(iso.year(Temporal.PlainDateTime.from("1994-11-05T08:15:30")), res, "PlainDateTime");
+assert.sameValue(iso.year(Temporal.PlainYearMonth.from("1994-11")), res, "PlainYearMonth");
+assert.sameValue(iso.year({ year: 1994, month: 11, day: 5 }), res, "property bag");
+assert.sameValue(iso.year("1994-11-05"), res, "string");
+assert.throws(TypeError, () => iso.year({ month: 5 }), "property bag with missing properties");
diff --git a/test/built-ins/Temporal/Calendar/prototype/year/builtin.js b/test/built-ins/Temporal/Calendar/prototype/year/builtin.js
new file mode 100644
index 00000000000..29e854ef1b9
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/year/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.year
+description: >
+ Tests that Temporal.Calendar.prototype.year
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.year),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.year),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.year),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.year.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/year/calendar-fields-iterable.js b/test/built-ins/Temporal/Calendar/prototype/year/calendar-fields-iterable.js
new file mode 100644
index 00000000000..191ade08570
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/year/calendar-fields-iterable.js
@@ -0,0 +1,34 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.year
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.calendar.prototype.year step 4:
+ 4. Return ? ISOYear(_dateOrDateTime_).
+ sec-temporal-isoyear step 1.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToListOfType(_fieldsArray_, « String »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+calendar1.year({ year: 2000, month: 5, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/Calendar/prototype/year/calendar-temporal-object.js b/test/built-ins/Temporal/Calendar/prototype/year/calendar-temporal-object.js
new file mode 100644
index 00000000000..25d1dd033a7
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/year/calendar-temporal-object.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dayofweek
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.calendar.prototype.year step 4:
+ 4. Return ? ISOYear(_dateOrDateTime_).
+ sec-temporal-isoyear step 1.a:
+ a. Set _dateOrDateTime_ to ? ToTemporalDate(_dateOrDateTime_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const calendar = new Temporal.Calendar("iso8601");
+ calendar.year({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/year/infinity-throws-rangeerror.js b/test/built-ins/Temporal/Calendar/prototype/year/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..4a10ff281ef
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/year/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.year
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.year({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.year({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/year/length.js b/test/built-ins/Temporal/Calendar/prototype/year/length.js
new file mode 100644
index 00000000000..4135ce8f6a0
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/year/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.year
+description: Temporal.Calendar.prototype.year.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.year, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/year/name.js b/test/built-ins/Temporal/Calendar/prototype/year/name.js
new file mode 100644
index 00000000000..a04a30a552b
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/year/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.year
+description: Temporal.Calendar.prototype.year.name is "year".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.year, "name", {
+ value: "year",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/year/not-a-constructor.js b/test/built-ins/Temporal/Calendar/prototype/year/not-a-constructor.js
new file mode 100644
index 00000000000..bd02ea43db3
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/year/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.year
+description: >
+ Temporal.Calendar.prototype.year does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.year();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.year), false,
+ "isConstructor(Temporal.Calendar.prototype.year)");
diff --git a/test/built-ins/Temporal/Calendar/prototype/year/prop-desc.js b/test/built-ins/Temporal/Calendar/prototype/year/prop-desc.js
new file mode 100644
index 00000000000..67b9dc227da
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/year/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.year
+description: The "year" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.year,
+ "function",
+ "`typeof Calendar.prototype.year` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "year", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/builtin.js b/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/builtin.js
new file mode 100644
index 00000000000..1e13c4bf666
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.yearmonthfromfields
+description: >
+ Tests that Temporal.Calendar.prototype.yearMonthFromFields
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.yearMonthFromFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.yearMonthFromFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.yearMonthFromFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.yearMonthFromFields.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/fields-not-object.js b/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/fields-not-object.js
new file mode 100644
index 00000000000..7969ba6e441
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/fields-not-object.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.yearmonthfromfields
+description: Throw a TypeError if the fields is not an object
+features: [Symbol, Temporal]
+---*/
+
+const tests = [undefined, null, false, "string", Symbol("sym"), Math.PI, 42n];
+const iso = Temporal.Calendar.from("iso8601");
+for (const fields of tests) {
+ assert.throws(TypeError, () => iso.yearMonthFromFields(fields, {}));
+}
diff --git a/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/infinity-throws-rangeerror.js b/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..374fd7fec1f
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/infinity-throws-rangeerror.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.yearmonthfromfields
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("iso8601");
+const base = { year: 2000, month: 5 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.yearMonthFromFields({ ...base, [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.yearMonthFromFields({ ...base, [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/length.js b/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/length.js
new file mode 100644
index 00000000000..d97ce84ca54
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.yearmonthfromfields
+description: Temporal.Calendar.prototype.yearMonthFromFields.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.yearMonthFromFields, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/name.js b/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/name.js
new file mode 100644
index 00000000000..803572242c4
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.yearmonthfromfields
+description: Temporal.Calendar.prototype.yearMonthFromFields.name is "yearMonthFromFields".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.yearMonthFromFields, "name", {
+ value: "yearMonthFromFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/not-a-constructor.js b/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/not-a-constructor.js
new file mode 100644
index 00000000000..7b7646f4c0d
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.yearmonthfromfields
+description: >
+ Temporal.Calendar.prototype.yearMonthFromFields does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.yearMonthFromFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.yearMonthFromFields), false,
+ "isConstructor(Temporal.Calendar.prototype.yearMonthFromFields)");
diff --git a/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-invalid-string.js b/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-invalid-string.js
new file mode 100644
index 00000000000..99ffe6af277
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-invalid-string.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.yearmonthfromfields
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.calendar.prototype.yearmonthfromfields step 6:
+ 6. Let _result_ be ? ISOYearMonthFromFields(_fields_, _options_).
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+assert.throws(RangeError, () => calendar.yearMonthFromFields({ year: 2000, month: 5 }, { overflow: "other string" }));
diff --git a/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-undefined.js b/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-undefined.js
new file mode 100644
index 00000000000..1f89b28aa47
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.yearmonthfromfields
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.calendar.prototype.yearmonthfromfields step 6:
+ 6. Let _result_ be ? ISOYearMonthFromFields(_fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+
+const explicit = calendar.yearMonthFromFields({ year: 2000, month: 15 }, { overflow: undefined });
+TemporalHelpers.assertPlainYearMonth(explicit, 2000, 12, "M12", "default overflow is constrain");
+const implicit = calendar.yearMonthFromFields({ year: 2000, month: 15 }, {});
+TemporalHelpers.assertPlainYearMonth(implicit, 2000, 12, "M12", "default overflow is constrain");
diff --git a/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-wrong-type.js b/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-wrong-type.js
new file mode 100644
index 00000000000..745b8cd7b2a
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/overflow-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.yearmonthfromfields
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.calendar.prototype.yearmonthfromfields step 6:
+ 6. Let _result_ be ? ISOYearMonthFromFields(_fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => calendar.yearMonthFromFields({ year: 2000, month: 5 }, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainYearMonth(result, 2000, 5, "M05", descr),
+);
diff --git a/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/prop-desc.js b/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/prop-desc.js
new file mode 100644
index 00000000000..3eedbcef62b
--- /dev/null
+++ b/test/built-ins/Temporal/Calendar/prototype/yearMonthFromFields/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.yearmonthfromfields
+description: The "yearMonthFromFields" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.yearMonthFromFields,
+ "function",
+ "`typeof Calendar.prototype.yearMonthFromFields` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "yearMonthFromFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/builtin.js b/test/built-ins/Temporal/Duration/builtin.js
new file mode 100644
index 00000000000..51a25f705e0
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/builtin.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration
+description: Tests that Temporal.Duration meets the requirements for built-in objects
+info: |
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Duration),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.Duration.prototype,
+ "object", "prototype property");
diff --git a/test/built-ins/Temporal/Duration/compare/argument-string-negative-fractional-units.js b/test/built-ins/Temporal/Duration/compare/argument-string-negative-fractional-units.js
new file mode 100644
index 00000000000..d5552dab186
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/argument-string-negative-fractional-units.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: Strings with fractional duration units are treated with the correct sign
+features: [Temporal]
+---*/
+
+const expectedHours = new Temporal.Duration(0, 0, 0, 0, -24, -34, -4, -404, -442, -799);
+const resultHours1 = Temporal.Duration.compare("-PT24.567890123H", expectedHours);
+assert.sameValue(resultHours1, 0, "negative fractional hours (first argument)");
+const resultHours2 = Temporal.Duration.compare(expectedHours, "-PT24.567890123H");
+assert.sameValue(resultHours2, 0, "negative fractional hours (second argument)");
+
+const expectedMinutes = new Temporal.Duration(0, 0, 0, 0, 0, -1440, -34, -73, -407, -379);
+const resultMinutes1 = Temporal.Duration.compare("-PT1440.567890123M", expectedMinutes);
+assert.sameValue(resultMinutes1, 0, "negative fractional minutes (first argument)");
+const resultMinutes2 = Temporal.Duration.compare("-PT1440.567890123M", expectedMinutes);
+assert.sameValue(resultMinutes2, 0, "negative fractional minutes (second argument)");
diff --git a/test/built-ins/Temporal/Duration/compare/builtin.js b/test/built-ins/Temporal/Duration/compare/builtin.js
new file mode 100644
index 00000000000..82f19478c0d
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/builtin.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: Tests that Temporal.Duration.compare meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Duration.compare),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.compare),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.compare),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.compare.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Duration/compare/calendar-fields-iterable.js b/test/built-ins/Temporal/Duration/compare/calendar-fields-iterable.js
new file mode 100644
index 00000000000..5beaa79ee93
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/calendar-fields-iterable.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.duration.compare step 4:
+ 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const duration1 = new Temporal.Duration(1);
+const duration2 = new Temporal.Duration(0, 12);
+Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar } });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/Duration/compare/calendar-temporal-object.js b/test/built-ins/Temporal/Duration/compare/calendar-temporal-object.js
new file mode 100644
index 00000000000..5db4212a3e0
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/calendar-temporal-object.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.duration.compare step 4:
+ 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(0, 12);
+ Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar: temporalObject } });
+});
diff --git a/test/built-ins/Temporal/Duration/compare/length.js b/test/built-ins/Temporal/Duration/compare/length.js
new file mode 100644
index 00000000000..44bed32c9af
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: Temporal.Duration.compare.length is 2
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.compare, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/compare/name.js b/test/built-ins/Temporal/Duration/compare/name.js
new file mode 100644
index 00000000000..ec6cd94c342
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: Temporal.Duration.compare.name is "compare".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.compare, "name", {
+ value: "compare",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/compare/not-a-constructor.js b/test/built-ins/Temporal/Duration/compare/not-a-constructor.js
new file mode 100644
index 00000000000..a8d5496c4ca
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/not-a-constructor.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: Temporal.Duration.compare does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Duration.compare();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.compare), false,
+ "isConstructor(Temporal.Duration.compare)");
diff --git a/test/built-ins/Temporal/Duration/compare/options-undefined.js b/test/built-ins/Temporal/Duration/compare/options-undefined.js
new file mode 100644
index 00000000000..a83d83e77c1
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/options-undefined.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const duration1 = new Temporal.Duration(1);
+const duration2 = new Temporal.Duration(0, 12);
+assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2), "default relativeTo is undefined");
+assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, undefined), "default relativeTo is undefined");
diff --git a/test/built-ins/Temporal/Duration/compare/prop-desc.js b/test/built-ins/Temporal/Duration/compare/prop-desc.js
new file mode 100644
index 00000000000..64526118346
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: The "compare" property of Temporal.Duration
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.compare,
+ "function",
+ "`typeof Duration.compare` is `function`"
+);
+
+verifyProperty(Temporal.Duration, "compare", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/compare/read-time-fields-before-datefromfields.js b/test/built-ins/Temporal/Duration/compare/read-time-fields-before-datefromfields.js
new file mode 100644
index 00000000000..b2b9f805e6d
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/read-time-fields-before-datefromfields.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.duration.compare step 4:
+ 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.g:
+ g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInvalidGettersTime();
+const duration1 = new Temporal.Duration(1);
+const duration2 = new Temporal.Duration(0, 12);
+Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar } });
diff --git a/test/built-ins/Temporal/Duration/compare/relativeto-propertybag-infinity-throws-rangeerror.js b/test/built-ins/Temporal/Duration/compare/relativeto-propertybag-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..425a3c40d91
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/relativeto-propertybag-infinity-throws-rangeerror.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the relativeTo property bag is Infinity or -Infinity
+esid: sec-temporal.duration.compare
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration1 = new Temporal.Duration(0, 0, 0, 1);
+const duration2 = new Temporal.Duration(0, 0, 0, 0, 24);
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo: { ...base, [prop]: inf } }), `${prop} property cannot be ${inf} in relativeTo`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo: { ...base, [prop]: obj } }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..4c87850b729
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(1, 1);
+ assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
diff --git a/test/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..651ac2bcf3b
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(1, 1);
+ assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
diff --git a/test/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..d9c89b348a2
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(1, 1);
+ assert.throws(TypeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
diff --git a/test/built-ins/Temporal/Duration/compare/relativeto-string-invalid.js b/test/built-ins/Temporal/Duration/compare/relativeto-string-invalid.js
new file mode 100644
index 00000000000..e4e2377dc64
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/relativeto-string-invalid.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: RangeError thrown if relativeTo is a string with the wrong format
+features: [Temporal]
+---*/
+
+['bad string', '15:30:45.123456', 'iso8601', 'UTC', 'P1YT1H'].forEach((relativeTo) => {
+ const duration1 = new Temporal.Duration(0, 0, 0, 31);
+ const duration2 = new Temporal.Duration(0, 1);
+ assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo }));
+});
diff --git a/test/built-ins/Temporal/Duration/compare/relativeto-string-plaindatetime.js b/test/built-ins/Temporal/Duration/compare/relativeto-string-plaindatetime.js
new file mode 100644
index 00000000000..b036ec286b7
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/relativeto-string-plaindatetime.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: The relativeTo option accepts a PlainDateTime-like ISO 8601 string
+features: [Temporal]
+---*/
+
+['2000-01-01', '2000-01-01T00:00', '2000-01-01T00:00[u-ca=iso8601]'].forEach((relativeTo) => {
+ const duration1 = new Temporal.Duration(0, 0, 0, 31);
+ const duration2 = new Temporal.Duration(0, 1);
+ const result = Temporal.Duration.compare(duration1, duration2, { relativeTo });
+ assert.sameValue(result, 0);
+});
diff --git a/test/built-ins/Temporal/Duration/compare/relativeto-string-zoneddatetime.js b/test/built-ins/Temporal/Duration/compare/relativeto-string-zoneddatetime.js
new file mode 100644
index 00000000000..1ddd14305d9
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/relativeto-string-zoneddatetime.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: The relativeTo option accepts a ZonedDateTime-like ISO 8601 string
+features: [Temporal]
+---*/
+
+[
+ '2000-01-01[UTC]',
+ '2000-01-01T00:00[UTC]',
+ '2000-01-01T00:00+00:00[UTC]',
+ '2000-01-01T00:00+00:00[UTC][u-ca=iso8601]',
+].forEach((relativeTo) => {
+ const duration1 = new Temporal.Duration(0, 0, 0, 31);
+ const duration2 = new Temporal.Duration(0, 1);
+ const result = Temporal.Duration.compare(duration1, duration2, { relativeTo });
+ assert.sameValue(result, 0);
+});
diff --git a/test/built-ins/Temporal/Duration/compare/relativeto-undefined-throw-on-calendar-units.js b/test/built-ins/Temporal/Duration/compare/relativeto-undefined-throw-on-calendar-units.js
new file mode 100644
index 00000000000..b2134635e37
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/relativeto-undefined-throw-on-calendar-units.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: >
+ The relativeTo option is required when either Duration contains years,
+ months, or weeks
+features: [Temporal]
+---*/
+
+const oneYear = new Temporal.Duration(1);
+const oneMonth = new Temporal.Duration(0, 1);
+const oneWeek = new Temporal.Duration(0, 0, 1);
+const oneDay = new Temporal.Duration(0, 0, 0, 1);
+
+assert.sameValue(Temporal.Duration.compare(oneDay, oneDay), 0, "days do not require relativeTo");
+
+assert.throws(RangeError, () => Temporal.Duration.compare(oneWeek, oneDay), "weeks in left operand require relativeTo");
+assert.throws(RangeError, () => Temporal.Duration.compare(oneDay, oneWeek), "weeks in right operand require relativeTo");
+
+assert.throws(RangeError, () => Temporal.Duration.compare(oneMonth, oneDay), "months in left operand require relativeTo");
+assert.throws(RangeError, () => Temporal.Duration.compare(oneDay, oneMonth), "months in right operand require relativeTo");
+
+assert.throws(RangeError, () => Temporal.Duration.compare(oneYear, oneDay), "years in left operand require relativeTo");
+assert.throws(RangeError, () => Temporal.Duration.compare(oneDay, oneYear), "years in right operand require relativeTo");
diff --git a/test/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-negative-epochnanoseconds.js b/test/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 00000000000..5df910ab4f4
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const relativeTo = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+const duration = new Temporal.Duration(0, 0, 0, 1);
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time; in this
+// case via relativeTo.
+
+const result = Temporal.Duration.compare(duration, duration, { relativeTo });
+assert.sameValue(result, 0);
diff --git a/test/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..7c514caaca2
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(1, 1);
+ assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo }));
+});
diff --git a/test/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..1b540bcbb26
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(1, 1);
+ assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo }));
+});
diff --git a/test/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..20050493e0e
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(1, 1);
+ assert.throws(TypeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo }));
+});
diff --git a/test/built-ins/Temporal/Duration/compare/timezone-getpossibleinstantsfor-iterable.js b/test/built-ins/Temporal/Duration/compare/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 00000000000..c07fefa6c65
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,38 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.duration.compare steps 4–6:
+ 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ 5. Let _shift1_ be ! CalculateOffsetShift(_relativeTo_, _one_.[[Years]], [...], _one_.[[Nanoseconds]]).
+ 6. Let _shift2_ be ! CalculateOffsetShift(_relativeTo_, _two_.[[Years]], [...], _two_.[[Nanoseconds]]).
+ sec-temporal-torelativetemporalobject step 6.d:
+ d. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNs_, _timeZone_, *"compatible"*, *"reject"*).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-calculateoffsetshift step 4:
+ 4. Let _after_ be ? AddZonedDateTime(_relativeTo_.[[Nanoseconds]], _relativeTo_.[[TimeZone]], _relativeTo_.[[Calendar]], _y_, [...], _ns_).
+ sec-temporal-addzoneddatetime step 8:
+ 8. Let _intermediateInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _intermediateDateTime_, *"compatible"*).
+ sec-temporal-builtintimezonegetinstantfor step 1:
+ 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "2000-01-01T00:00:00", // called once on the input relativeTo object
+ "2001-01-01T00:00:00", // called once on relativeTo plus the first operand
+ "2001-02-01T00:00:00", // called once on relativeTo plus the second operand
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(0, 13);
+ Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 1, day: 1, timeZone } });
+}, expected);
diff --git a/test/built-ins/Temporal/Duration/compare/timezone-string-datetime.js b/test/built-ins/Temporal/Duration/compare/timezone-string-datetime.js
new file mode 100644
index 00000000000..7dcb57dbe44
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/compare/timezone-string-datetime.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.compare
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "bare date-time string is not a time zone");
+assert.throws(RangeError, () => Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), "bare date-time string is not a time zone");
+
+// The following are all valid strings so should not throw:
+
+[
+ "2021-08-19T17:30Z",
+ "2021-08-19T17:30-07:00",
+ "2021-08-19T17:30[America/Vancouver]",
+ "2021-08-19T17:30Z[America/Vancouver]",
+ "2021-08-19T17:30-07:00[America/Vancouver]",
+].forEach((timeZone) => {
+ Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
+ Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } });
+});
diff --git a/test/built-ins/Temporal/Duration/constructor.js b/test/built-ins/Temporal/Duration/constructor.js
new file mode 100644
index 00000000000..7406a00c6f7
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/constructor.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration
+description: Temporal.Duration constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.Duration());
diff --git a/test/built-ins/Temporal/Duration/days-undefined.js b/test/built-ins/Temporal/Duration/days-undefined.js
new file mode 100644
index 00000000000..716ad626f5a
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/days-undefined.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration
+description: Undefined arguments should be treated as zero.
+features: [Temporal]
+---*/
+
+const args = [1, 1, 1];
+
+const explicit = new Temporal.Duration(...args, undefined);
+assert.sameValue(explicit.days, 0, "days default argument");
+
+const implicit = new Temporal.Duration(...args);
+assert.sameValue(implicit.days, 0, "days default argument");
diff --git a/test/built-ins/Temporal/Duration/from/argument-existing-object.js b/test/built-ins/Temporal/Duration/from/argument-existing-object.js
new file mode 100644
index 00000000000..8173f7c96cd
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/from/argument-existing-object.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.from
+description: Property bag is converted to Duration; Duration is copied
+features: [Temporal]
+---*/
+
+const d1 = Temporal.Duration.from({ milliseconds: 1000 });
+assert.sameValue(d1.seconds, 0);
+assert.sameValue(d1.milliseconds, 1000);
+
+const d2 = Temporal.Duration.from(d1);
+assert.notSameValue(d1, d2);
+assert.sameValue(d1.seconds, 0);
+assert.sameValue(d1.milliseconds, 1000);
+assert.sameValue(d2.seconds, 0);
+assert.sameValue(d2.milliseconds, 1000);
diff --git a/test/built-ins/Temporal/Duration/from/argument-non-string.js b/test/built-ins/Temporal/Duration/from/argument-non-string.js
new file mode 100644
index 00000000000..5b5a41211c3
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/from/argument-non-string.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.from
+description: Appropriate error thrown if primitive input cannot convert to a valid string
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => Temporal.Duration.from(undefined), "undefined");
+assert.throws(RangeError, () => Temporal.Duration.from(null), "null");
+assert.throws(RangeError, () => Temporal.Duration.from(true), "boolean");
+assert.throws(TypeError, () => Temporal.Duration.from(Symbol()), "Symbol");
+assert.throws(RangeError, () => Temporal.Duration.from(5), "number");
+assert.throws(RangeError, () => Temporal.Duration.from(5n), "bigint");
diff --git a/test/built-ins/Temporal/Duration/from/argument-string-negative-fractional-units.js b/test/built-ins/Temporal/Duration/from/argument-string-negative-fractional-units.js
new file mode 100644
index 00000000000..b9d66b3a7f4
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/from/argument-string-negative-fractional-units.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const resultHours = Temporal.Duration.from("-PT24.567890123H");
+TemporalHelpers.assertDuration(resultHours, 0, 0, 0, 0, -24, -34, -4, -404, -442, -799, "negative fractional hours");
+
+const resultMinutes = Temporal.Duration.from("-PT1440.567890123M");
+TemporalHelpers.assertDuration(resultMinutes, 0, 0, 0, 0, 0, -1440, -34, -73, -407, -379, "negative fractional minutes");
diff --git a/test/built-ins/Temporal/Duration/from/builtin.js b/test/built-ins/Temporal/Duration/from/builtin.js
new file mode 100644
index 00000000000..af6da2acd58
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/from/builtin.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.from
+description: Tests that Temporal.Duration.from meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Duration.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.from.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Duration/from/infinity-throws-rangeerror.js b/test/built-ins/Temporal/Duration/from/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..68dd6167bb5
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/from/infinity-throws-rangeerror.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.Duration.from handles a property bag if any value is Infinity
+esid: sec-temporal.duration.from
+features: [Temporal]
+---*/
+
+const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => Temporal.Duration.from({ [field]: Infinity }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => Temporal.Duration.from({ [field]: obj }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
diff --git a/test/built-ins/Temporal/Duration/from/length.js b/test/built-ins/Temporal/Duration/from/length.js
new file mode 100644
index 00000000000..9dc463a5ffc
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/from/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.from
+description: Temporal.Duration.from.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/from/name.js b/test/built-ins/Temporal/Duration/from/name.js
new file mode 100644
index 00000000000..5879cc82bfc
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/from/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.from
+description: Temporal.Duration.from.name is "from"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/from/negative-inifinity-throws-rangeerror.js b/test/built-ins/Temporal/Duration/from/negative-inifinity-throws-rangeerror.js
new file mode 100644
index 00000000000..ee95e13effb
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/from/negative-inifinity-throws-rangeerror.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.Duration.from handles a property bag if any value is -Infinity
+esid: sec-temporal.duration.from
+features: [Temporal]
+---*/
+
+const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => Temporal.Duration.from({ [field]: -Infinity }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => Temporal.Duration.from({ [field]: obj }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
diff --git a/test/built-ins/Temporal/Duration/from/non-integer-throws-rangeerror.js b/test/built-ins/Temporal/Duration/from/non-integer-throws-rangeerror.js
new file mode 100644
index 00000000000..661936ac72c
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/from/non-integer-throws-rangeerror.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.with
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => Temporal.Duration.from({ [field]: 1.5 }));
+ assert.throws(RangeError, () => Temporal.Duration.from({ [field]: -1.5 }));
+});
diff --git a/test/built-ins/Temporal/Duration/from/not-a-constructor.js b/test/built-ins/Temporal/Duration/from/not-a-constructor.js
new file mode 100644
index 00000000000..8c2e16a9b4c
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/from/not-a-constructor.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.from
+description: Temporal.Duration.from does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Duration.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.from), false,
+ "isConstructor(Temporal.Duration.from)");
diff --git a/test/built-ins/Temporal/Duration/from/order-of-operations.js b/test/built-ins/Temporal/Duration/from/order-of-operations.js
new file mode 100644
index 00000000000..308b483b8c3
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/from/order-of-operations.js
@@ -0,0 +1,69 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.from
+description: Properties on an object passed to from() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get days",
+ "get days.valueOf",
+ "call days.valueOf",
+ "get hours",
+ "get hours.valueOf",
+ "call hours.valueOf",
+ "get microseconds",
+ "get microseconds.valueOf",
+ "call microseconds.valueOf",
+ "get milliseconds",
+ "get milliseconds.valueOf",
+ "call milliseconds.valueOf",
+ "get minutes",
+ "get minutes.valueOf",
+ "call minutes.valueOf",
+ "get months",
+ "get months.valueOf",
+ "call months.valueOf",
+ "get nanoseconds",
+ "get nanoseconds.valueOf",
+ "call nanoseconds.valueOf",
+ "get seconds",
+ "get seconds.valueOf",
+ "call seconds.valueOf",
+ "get weeks",
+ "get weeks.valueOf",
+ "call weeks.valueOf",
+ "get years",
+ "get years.valueOf",
+ "call years.valueOf",
+];
+const actual = [];
+const fields = {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = Temporal.Duration.from(argument);
+TemporalHelpers.assertDuration(result, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/Duration/from/prop-desc.js b/test/built-ins/Temporal/Duration/from/prop-desc.js
new file mode 100644
index 00000000000..3fd679a21f0
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/from/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.from
+description: The "from" property of Temporal.Duration
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.from,
+ "function",
+ "`typeof Duration.from` is `function`"
+);
+
+verifyProperty(Temporal.Duration, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/from/string-with-skipped-units.js b/test/built-ins/Temporal/Duration/from/string-with-skipped-units.js
new file mode 100644
index 00000000000..cf8e48f6492
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/from/string-with-skipped-units.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.from
+description: |
+ Creating a Duration from an ISO 8601 string with an absent designator between
+ two other designators
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Date designators: years-weeks (months missing)
+
+const d1 = Temporal.Duration.from("P3Y4W");
+TemporalHelpers.assertDuration(d1, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0, "years-weeks string");
+
+// Date designators: years-days (months and weeks missing)
+
+const d2 = Temporal.Duration.from("P3Y4D");
+TemporalHelpers.assertDuration(d2, 3, 0, 0, 4, 0, 0, 0, 0, 0, 0, "years-days string");
+
+// Date designators: months-days (weeks missing)
+
+const d3 = Temporal.Duration.from("P3M4D");
+TemporalHelpers.assertDuration(d3, 0, 3, 0, 4, 0, 0, 0, 0, 0, 0, "months-days string");
+
+// Time designators: hours-seconds (minutes missing)
+
+const d4 = Temporal.Duration.from("PT3H4.123456789S");
+TemporalHelpers.assertDuration(d4, 0, 0, 0, 0, 3, 0, 4, 123, 456, 789, "hours-seconds string");
diff --git a/test/built-ins/Temporal/Duration/from/subclassing-ignored.js b/test/built-ins/Temporal/Duration/from/subclassing-ignored.js
new file mode 100644
index 00000000000..bb75f3836f3
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/from/subclassing-ignored.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.Duration,
+ "from",
+ ["P1Y2M3W4DT5H6M7.987654321S"],
+ (result) => TemporalHelpers.assertDuration(result, 1, 2, 3, 4, 5, 6, 7, 987, 654, 321),
+);
diff --git a/test/built-ins/Temporal/Duration/hours-undefined.js b/test/built-ins/Temporal/Duration/hours-undefined.js
new file mode 100644
index 00000000000..26696569382
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/hours-undefined.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration
+description: Undefined arguments should be treated as zero.
+features: [Temporal]
+---*/
+
+const args = [1, 1, 1, 1];
+
+const explicit = new Temporal.Duration(...args, undefined);
+assert.sameValue(explicit.hours, 0, "hours default argument");
+
+const implicit = new Temporal.Duration(...args);
+assert.sameValue(implicit.hours, 0, "hours default argument");
diff --git a/test/built-ins/Temporal/Duration/infinity-throws-rangeerror.js b/test/built-ins/Temporal/Duration/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..cd6a7f7bcd3
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/infinity-throws-rangeerror.js
@@ -0,0 +1,81 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.Duration throws a RangeError if any value is Infinity
+esid: sec-temporal.duration
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.Duration(Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite years",
+ [O(Infinity, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf"]
+ ],
+ [
+ "infinite months",
+ [O(0, "years"), O(Infinity, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf"]
+ ],
+ [
+ "infinite weeks",
+ [O(0, "years"), O(0, "months"), O(Infinity, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf"]
+ ],
+ [
+ "infinite days",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(Infinity, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf"]
+ ],
+ [
+ "infinite hours",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(Infinity, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf"]
+ ],
+ [
+ "infinite minutes",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(Infinity, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf"]
+ ],
+ [
+ "infinite seconds",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(Infinity, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf"]
+ ],
+ [
+ "infinite milliseconds",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(Infinity, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf", "get milliseconds.valueOf", "call milliseconds.valueOf"]
+ ],
+ [
+ "infinite microseconds",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(Infinity, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf", "get milliseconds.valueOf", "call milliseconds.valueOf", "get microseconds.valueOf", "call microseconds.valueOf"]
+ ],
+ [
+ "infinite nanoseconds",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(Infinity, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf", "get milliseconds.valueOf", "call milliseconds.valueOf", "get microseconds.valueOf", "call microseconds.valueOf", "get nanoseconds.valueOf", "call nanoseconds.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.Duration(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
diff --git a/test/built-ins/Temporal/Duration/length.js b/test/built-ins/Temporal/Duration/length.js
new file mode 100644
index 00000000000..36638e37040
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration
+description: Temporal.Duration.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/microseconds-undefined.js b/test/built-ins/Temporal/Duration/microseconds-undefined.js
new file mode 100644
index 00000000000..e0f92cb967c
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/microseconds-undefined.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration
+description: Undefined arguments should be treated as zero.
+features: [Temporal]
+---*/
+
+const args = [1, 1, 1, 1, 1, 1, 1, 1];
+
+const explicit = new Temporal.Duration(...args, undefined);
+assert.sameValue(explicit.microseconds, 0, "microseconds default argument");
+
+const implicit = new Temporal.Duration(...args);
+assert.sameValue(implicit.microseconds, 0, "microseconds default argument");
diff --git a/test/built-ins/Temporal/Duration/milliseconds-undefined.js b/test/built-ins/Temporal/Duration/milliseconds-undefined.js
new file mode 100644
index 00000000000..c460c6a06ee
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/milliseconds-undefined.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration
+description: Undefined arguments should be treated as zero.
+features: [Temporal]
+---*/
+
+const args = [1, 1, 1, 1, 1, 1, 1];
+
+const explicit = new Temporal.Duration(...args, undefined);
+assert.sameValue(explicit.milliseconds, 0, "milliseconds default argument");
+
+const implicit = new Temporal.Duration(...args);
+assert.sameValue(implicit.milliseconds, 0, "milliseconds default argument");
diff --git a/test/built-ins/Temporal/Duration/minutes-undefined.js b/test/built-ins/Temporal/Duration/minutes-undefined.js
new file mode 100644
index 00000000000..265bd4fbc6b
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/minutes-undefined.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration
+description: Undefined arguments should be treated as zero.
+features: [Temporal]
+---*/
+
+const args = [1, 1, 1, 1, 1];
+
+const explicit = new Temporal.Duration(...args, undefined);
+assert.sameValue(explicit.minutes, 0, "minutes default argument");
+
+const implicit = new Temporal.Duration(...args);
+assert.sameValue(implicit.minutes, 0, "minutes default argument");
diff --git a/test/built-ins/Temporal/Duration/months-undefined.js b/test/built-ins/Temporal/Duration/months-undefined.js
new file mode 100644
index 00000000000..f9cad3f216c
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/months-undefined.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration
+description: Undefined arguments should be treated as zero.
+features: [Temporal]
+---*/
+
+const years = 1;
+
+const explicit = new Temporal.Duration(years, undefined);
+assert.sameValue(explicit.months, 0, "months default argument");
+
+const implicit = new Temporal.Duration(years);
+assert.sameValue(implicit.months, 0, "months default argument");
diff --git a/test/built-ins/Temporal/Duration/name.js b/test/built-ins/Temporal/Duration/name.js
new file mode 100644
index 00000000000..6455b408eaa
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration
+description: Temporal.Duration.name is "Duration"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration, "name", {
+ value: "Duration",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/nanoseconds-undefined.js b/test/built-ins/Temporal/Duration/nanoseconds-undefined.js
new file mode 100644
index 00000000000..2d4de39199c
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/nanoseconds-undefined.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration
+description: Undefined arguments should be treated as zero.
+features: [Temporal]
+---*/
+
+const args = [1, 1, 1, 1, 1, 1, 1, 1, 1];
+
+const explicit = new Temporal.Duration(...args, undefined);
+assert.sameValue(explicit.nanoseconds, 0, "nanoseconds default argument");
+
+const implicit = new Temporal.Duration(...args);
+assert.sameValue(implicit.nanoseconds, 0, "nanoseconds default argument");
diff --git a/test/built-ins/Temporal/Duration/negative-infinity-throws-rangeerror.js b/test/built-ins/Temporal/Duration/negative-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..53c0ebe74b6
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,81 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.Duration throws a RangeError if any value is -Infinity
+esid: sec-temporal.duration
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.Duration(-Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, -Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite years",
+ [O(-Infinity, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf"]
+ ],
+ [
+ "infinite months",
+ [O(0, "years"), O(-Infinity, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf"]
+ ],
+ [
+ "infinite weeks",
+ [O(0, "years"), O(0, "months"), O(-Infinity, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf"]
+ ],
+ [
+ "infinite days",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(-Infinity, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf"]
+ ],
+ [
+ "infinite hours",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(-Infinity, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf"]
+ ],
+ [
+ "infinite minutes",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(-Infinity, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf"]
+ ],
+ [
+ "infinite seconds",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(-Infinity, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf"]
+ ],
+ [
+ "infinite milliseconds",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(-Infinity, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf", "get milliseconds.valueOf", "call milliseconds.valueOf"]
+ ],
+ [
+ "infinite microseconds",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(-Infinity, "microseconds"), O(0, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf", "get milliseconds.valueOf", "call milliseconds.valueOf", "get microseconds.valueOf", "call microseconds.valueOf"]
+ ],
+ [
+ "infinite nanoseconds",
+ [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(-Infinity, "nanoseconds")],
+ ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf", "get milliseconds.valueOf", "call milliseconds.valueOf", "get microseconds.valueOf", "call microseconds.valueOf", "get nanoseconds.valueOf", "call nanoseconds.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.Duration(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
diff --git a/test/built-ins/Temporal/Duration/prop-desc.js b/test/built-ins/Temporal/Duration/prop-desc.js
new file mode 100644
index 00000000000..7a3494e8fc6
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration
+description: The "Duration" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration,
+ "function",
+ "`typeof Duration` is `function`"
+);
+
+verifyProperty(Temporal, "Duration", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/abs/builtin.js b/test/built-ins/Temporal/Duration/prototype/abs/builtin.js
new file mode 100644
index 00000000000..d4f97bad9e4
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/abs/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.abs
+description: >
+ Tests that Temporal.Duration.prototype.abs
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Duration.prototype.abs),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.abs),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.abs),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.prototype.abs.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Duration/prototype/abs/length.js b/test/built-ins/Temporal/Duration/prototype/abs/length.js
new file mode 100644
index 00000000000..873127cc260
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/abs/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.abs
+description: Temporal.Duration.prototype.abs.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype.abs, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/abs/name.js b/test/built-ins/Temporal/Duration/prototype/abs/name.js
new file mode 100644
index 00000000000..68d8309c2d7
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/abs/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.abs
+description: Temporal.Duration.prototype.abs.name is "abs".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype.abs, "name", {
+ value: "abs",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/abs/not-a-constructor.js b/test/built-ins/Temporal/Duration/prototype/abs/not-a-constructor.js
new file mode 100644
index 00000000000..bf7cb628daf
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/abs/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.abs
+description: >
+ Temporal.Duration.prototype.abs does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Duration.prototype.abs();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.prototype.abs), false,
+ "isConstructor(Temporal.Duration.prototype.abs)");
diff --git a/test/built-ins/Temporal/Duration/prototype/abs/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/abs/prop-desc.js
new file mode 100644
index 00000000000..8230b9e1c6b
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/abs/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.abs
+description: The "abs" property of Temporal.Duration.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.prototype.abs,
+ "function",
+ "`typeof Duration.prototype.abs` is `function`"
+);
+
+verifyProperty(Temporal.Duration.prototype, "abs", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/abs/subclassing-ignored.js b/test/built-ins/Temporal/Duration/prototype/abs/subclassing-ignored.js
new file mode 100644
index 00000000000..ec26809989b
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/abs/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.abs
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.Duration,
+ [0, 0, 0, -4, -5, -6, -7, -987, -654, -321],
+ "abs",
+ [],
+ (result) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 5, 6, 7, 987, 654, 321),
+);
diff --git a/test/built-ins/Temporal/Duration/prototype/add/argument-string-negative-fractional-units.js b/test/built-ins/Temporal/Duration/prototype/add/argument-string-negative-fractional-units.js
new file mode 100644
index 00000000000..8c4b038a5d8
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/argument-string-negative-fractional-units.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration();
+
+const resultHours = instance.add("-PT24.567890123H");
+TemporalHelpers.assertDuration(resultHours, 0, 0, 0, 0, -24, -34, -4, -404, -442, -799, "negative fractional hours");
+
+const resultMinutes = instance.add("-PT1440.567890123M");
+TemporalHelpers.assertDuration(resultMinutes, 0, 0, 0, 0, 0, -1440, -34, -73, -407, -379, "negative fractional minutes");
diff --git a/test/built-ins/Temporal/Duration/prototype/add/balance-negative-result.js b/test/built-ins/Temporal/Duration/prototype/add/balance-negative-result.js
new file mode 100644
index 00000000000..30c164f7968
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/balance-negative-result.js
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: A negative duration result is balanced correctly by the modulo operation in NanosecondsToDays
+info: |
+ sec-temporal-nanosecondstodays step 6:
+ 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
+ sec-temporal-balanceduration step 4:
+ 4. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_).
+ sec-temporal-addduration steps 5–6:
+ 5. If _relativeTo_ is *undefined*, then
+ ...
+ b. Let _result_ be ! BalanceDuration(_d1_ + _d2_, _h1_ + _h2_, _min1_ + _min2_, _s1_ + _s2_, _ms1_ + _ms2_, _mus1_ + _mus2_, _ns1_ + _ns2_, _largestUnit_).
+ ...
+ 6. Else if _relativeTo_ has an [[InitializedTemporalPlainDateTime]] internal slot, then
+ ...
+ n. Let _result_ be ! BalanceDuration(_dateDifference_.[[Days]], _h1_ + _h2_, _min1_ + _min2_, _s1_ + _s2_, _ms1_ + _ms2_, _mus1_ + _mus2_, _ns1_ + _ns2_, _largestUnit_).
+ sec-temporal.duration.prototype.add step 6:
+ 6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _other_.[[Years]], _other_.[[Months]], _other_.[[Weeks]], _other_.[[Days]], _other_.[[Hours]], _other_.[[Minutes]], _other_.[[Seconds]], _other_.[[Milliseconds]], _other_.[[Microseconds]], _other_.[[Nanoseconds]], _relativeTo_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration1 = new Temporal.Duration(0, 0, 0, 0, -60);
+const duration2 = new Temporal.Duration(0, 0, 0, -1);
+
+const resultNotRelative = duration1.add(duration2);
+TemporalHelpers.assertDuration(resultNotRelative, 0, 0, 0, -3, -12, 0, 0, 0, 0, 0);
+
+const relativeTo = new Temporal.PlainDateTime(2000, 1, 1);
+const resultRelative = duration1.add(duration2, { relativeTo });
+TemporalHelpers.assertDuration(resultRelative, 0, 0, 0, -3, -12, 0, 0, 0, 0, 0);
diff --git a/test/built-ins/Temporal/Duration/prototype/add/balance-negative-time-units.js b/test/built-ins/Temporal/Duration/prototype/add/balance-negative-time-units.js
new file mode 100644
index 00000000000..06c068e210e
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/balance-negative-time-units.js
@@ -0,0 +1,60 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: Negative time fields in relativeTo are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-differencetime step 8:
+ 8. Let _bt_ be ? BalanceTime(_hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal-differenceisodatetime step 2:
+ 2. Let _timeDifference_ be ? DifferenceTime(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_).
+ sec-temporal-differencezoneddatetime step 7:
+ 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_).
+ sec-temporal-addduration step 7.g.i:
+ i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
+ sec-temporal.duration.prototype.add step 6:
+ 6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _other_.[[Years]], _other_.[[Months]], _other_.[[Weeks]], _other_.[[Days]], _other_.[[Hours]], _other_.[[Minutes]], _other_.[[Seconds]], _other_.[[Milliseconds]], _other_.[[Microseconds]], _other_.[[Nanoseconds]], _relativeTo_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 1, 1, 1, 1, 1, 1);
+
+const timeZone = new Temporal.TimeZone("UTC");
+const relativeTo = new Temporal.ZonedDateTime(830998861_000_000_000n, timeZone);
+// This code path is encountered if largestUnit is years, months, weeks, or days
+// and relativeTo is a ZonedDateTime
+const options = { largestUnit: "days", relativeTo };
+
+const result1 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -2), options);
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -2), options);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -2), options);
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, -2), options);
+TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, -2), options);
+TemporalHelpers.assertDuration(result5, 0, 0, 0, 0, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+// This one is different because hours are later balanced again in BalanceDuration
+const result6 = duration.add(new Temporal.Duration(0, 0, 0, 0, -2), options);
+TemporalHelpers.assertDuration(result6, 0, 0, 0, 0, 0, -58, -58, -998, -998, -999, "hours balance");
diff --git a/test/built-ins/Temporal/Duration/prototype/add/builtin.js b/test/built-ins/Temporal/Duration/prototype/add/builtin.js
new file mode 100644
index 00000000000..a13e72b6737
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: >
+ Tests that Temporal.Duration.prototype.add
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Duration.prototype.add),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.add),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.add),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.prototype.add.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Duration/prototype/add/calendar-dateadd-called-with-options-undefined.js b/test/built-ins/Temporal/Duration/prototype/add/calendar-dateadd-called-with-options-undefined.js
new file mode 100644
index 00000000000..84dfc1f0d21
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/calendar-dateadd-called-with-options-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: >
+ BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the
+ options value
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddUndefinedOptions();
+const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9);
+const instance = new Temporal.Duration(1, 1, 1, 1);
+instance.add(instance, { relativeTo: new Temporal.ZonedDateTime(0n, timeZone, calendar) });
+assert.sameValue(calendar.dateAddCallCount, 5);
diff --git a/test/built-ins/Temporal/Duration/prototype/add/calendar-dateuntil-called-with-singular-largestunit.js b/test/built-ins/Temporal/Duration/prototype/add/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 00000000000..ced56a74616
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,78 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.duration.prototype.add step 6:
+ 6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _other_.[[Years]], _other_.[[Months]], _other_.[[Weeks]], _other_.[[Days]], _other_.[[Hours]], _other_.[[Minutes]], _other_.[[Seconds]], _other_.[[Milliseconds]], _other_.[[Microseconds]], _other_.[[Nanoseconds]], _relativeTo_).
+ sec-temporal-addduration steps 6-7:
+ 6. If _relativeTo_ has an [[InitializedTemporalPlainDateTime]] internal slot, then
+ ...
+ j. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
+ k. Let _differenceOptions_ be ! OrdinaryObjectCreate(*null*).
+ l. Perform ! CreateDataPropertyOrThrow(_differenceOptions_, *"largestUnit"*, _dateLargestUnit_).
+ m. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _datePart_, _end_, _differenceOptions_).
+ ...
+ 7. Else,
+ a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot.
+ ...
+ f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ g. Else,
+ i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
+ sec-temporal-differencezoneddatetime steps 7 and 11:
+ 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_).
+ 11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_).
+ sec-temporal-nanosecondstodays step 11:
+ 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*).
+ sec-temporal-differenceisodatetime steps 9–11:
+ 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
+ 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
+ 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit, index) => {
+ const one = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(1)]);
+ const two = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(2)]);
+ const relativeTo = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+ one.add(two, { relativeTo, largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"],
+ weeks: ["week"],
+ days: ["day"],
+ hours: ["day"],
+ minutes: ["day"],
+ seconds: ["day"],
+ milliseconds: ["day"],
+ microseconds: ["day"],
+ nanoseconds: ["day"]
+ }
+);
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit, index) => {
+ const one = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(1)]);
+ const two = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(2)]);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+ one.add(two, { relativeTo, largestUnit });
+ },
+ {
+ years: ["year", "day"],
+ months: ["month", "day"],
+ weeks: ["week", "day"],
+ days: ["day", "day"],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
diff --git a/test/built-ins/Temporal/Duration/prototype/add/calendar-fields-iterable.js b/test/built-ins/Temporal/Duration/prototype/add/calendar-fields-iterable.js
new file mode 100644
index 00000000000..91309aa2a1f
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/calendar-fields-iterable.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.duration.prototype.add step 5:
+ 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const duration1 = new Temporal.Duration(1);
+const duration2 = new Temporal.Duration(0, 12);
+duration1.add(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar } });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/Duration/prototype/add/calendar-temporal-object.js b/test/built-ins/Temporal/Duration/prototype/add/calendar-temporal-object.js
new file mode 100644
index 00000000000..aaa960358ad
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/calendar-temporal-object.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.duration.prototype.add step 5:
+ 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2b
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(0, 12);
+ duration1.add(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar: temporalObject } });
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/add/infinity-throws-rangeerror.js b/test/built-ins/Temporal/Duration/prototype/add/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..502192562cb
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/infinity-throws-rangeerror.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.Duration.prototype.add throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.duration.prototype.add
+features: [Temporal]
+---*/
+
+const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const relativeTo = new Temporal.PlainDateTime(2000, 1, 1);
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: Infinity }, { relativeTo }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { relativeTo }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/add/length.js b/test/built-ins/Temporal/Duration/prototype/add/length.js
new file mode 100644
index 00000000000..93fb150628b
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: Temporal.Duration.prototype.add.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype.add, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/add/name.js b/test/built-ins/Temporal/Duration/prototype/add/name.js
new file mode 100644
index 00000000000..5d269602da3
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: Temporal.Duration.prototype.add.name is "add".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype.add, "name", {
+ value: "add",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/add/negative-infinity-throws-rangeerror.js b/test/built-ins/Temporal/Duration/prototype/add/negative-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..8ba38ab6bce
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.Duration.prototype.add throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.duration.prototype.add
+features: [Temporal]
+---*/
+
+const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const relativeTo = new Temporal.PlainDateTime(2000, 1, 1);
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: -Infinity }, { relativeTo }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { relativeTo }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/add/non-integer-throws-rangeerror.js b/test/built-ins/Temporal/Duration/prototype/add/non-integer-throws-rangeerror.js
new file mode 100644
index 00000000000..7b82df19311
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/non-integer-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 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 }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/add/not-a-constructor.js b/test/built-ins/Temporal/Duration/prototype/add/not-a-constructor.js
new file mode 100644
index 00000000000..a2273afd680
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: >
+ Temporal.Duration.prototype.add does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Duration.prototype.add();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.prototype.add), false,
+ "isConstructor(Temporal.Duration.prototype.add)");
diff --git a/test/built-ins/Temporal/Duration/prototype/add/options-undefined.js b/test/built-ins/Temporal/Duration/prototype/add/options-undefined.js
new file mode 100644
index 00000000000..ef9717d9ac2
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/options-undefined.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const duration1 = new Temporal.Duration(1);
+const duration2 = new Temporal.Duration(0, 12);
+assert.throws(RangeError, () => duration1.add(duration2), "default relativeTo is undefined");
+assert.throws(RangeError, () => duration1.add(duration2, undefined), "default relativeTo is undefined");
diff --git a/test/built-ins/Temporal/Duration/prototype/add/order-of-operations.js b/test/built-ins/Temporal/Duration/prototype/add/order-of-operations.js
new file mode 100644
index 00000000000..f44ea0845de
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/order-of-operations.js
@@ -0,0 +1,74 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: Properties on an object passed to add() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 2, 0, 4, 5, 6, 7, 987, 654, 321);
+const relativeTo = new Temporal.PlainDateTime(2000, 1, 1);
+const expected = [
+ "get days",
+ "get days.valueOf",
+ "call days.valueOf",
+ "get hours",
+ "get hours.valueOf",
+ "call hours.valueOf",
+ "get microseconds",
+ "get microseconds.valueOf",
+ "call microseconds.valueOf",
+ "get milliseconds",
+ "get milliseconds.valueOf",
+ "call milliseconds.valueOf",
+ "get minutes",
+ "get minutes.valueOf",
+ "call minutes.valueOf",
+ "get months",
+ "get months.valueOf",
+ "call months.valueOf",
+ "get nanoseconds",
+ "get nanoseconds.valueOf",
+ "call nanoseconds.valueOf",
+ "get seconds",
+ "get seconds.valueOf",
+ "call seconds.valueOf",
+ "get weeks",
+ "get weeks.valueOf",
+ "call weeks.valueOf",
+ "get years",
+ "get years.valueOf",
+ "call years.valueOf",
+];
+const actual = [];
+const fields = {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = instance.add(argument, { relativeTo });
+TemporalHelpers.assertDuration(result, 2, 3, 0, 12, 6, 7, 8, 988, 655, 322);
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/Duration/prototype/add/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/add/prop-desc.js
new file mode 100644
index 00000000000..506a9812584
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: The "add" property of Temporal.Duration.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.prototype.add,
+ "function",
+ "`typeof Duration.prototype.add` is `function`"
+);
+
+verifyProperty(Temporal.Duration.prototype, "add", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/add/read-time-fields-before-datefromfields.js b/test/built-ins/Temporal/Duration/prototype/add/read-time-fields-before-datefromfields.js
new file mode 100644
index 00000000000..0edbe3e00ac
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/read-time-fields-before-datefromfields.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.duration.prototype.add step 5:
+ 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.g:
+ g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInvalidGettersTime();
+const duration1 = new Temporal.Duration(1);
+const duration2 = new Temporal.Duration(0, 12);
+duration1.add(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar } });
diff --git a/test/built-ins/Temporal/Duration/prototype/add/relativeto-infinity-throws-rangeerror.js b/test/built-ins/Temporal/Duration/prototype/add/relativeto-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..187a3072001
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/relativeto-infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.duration.prototype.add
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.add(instance, { relativeTo: { ...base, [prop]: inf } }), `${prop} property cannot be ${inf} in relativeTo`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.add(instance, { relativeTo: { ...base, [prop]: obj } }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..6ff38ac14ec
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(2);
+ assert.throws(RangeError, () => duration.add(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..d34a9ea3164
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(2);
+ assert.throws(RangeError, () => duration.add(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..871d5f7dcc7
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(2);
+ assert.throws(TypeError, () => duration.add(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/add/relativeto-string-datetime.js b/test/built-ins/Temporal/Duration/prototype/add/relativeto-string-datetime.js
new file mode 100644
index 00000000000..4cbb6d57413
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/relativeto-string-datetime.js
@@ -0,0 +1,40 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: >
+ Conversion of ISO date-time strings as relativeTo option to
+ Temporal.ZonedDateTime or Temporal.PlainDateTime instances
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+let relativeTo = "2019-11-01T00:00";
+const result1 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+TemporalHelpers.assertDuration(result1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "bare date-time string is a plain relativeTo");
+
+relativeTo = "2019-11-01T00:00Z";
+const result2 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+TemporalHelpers.assertDuration(result2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z is a plain relativeTo");
+
+relativeTo = "2019-11-01T00:00-07:00";
+const result3 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+TemporalHelpers.assertDuration(result3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + offset is a plain relativeTo");
+
+relativeTo = "2019-11-01T00:00[America/Vancouver]";
+const result4 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+TemporalHelpers.assertDuration(result4, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, "date-time + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00Z[America/Vancouver]";
+const result5 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+TemporalHelpers.assertDuration(result5, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00-07:00[America/Vancouver]";
+const result6 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
+TemporalHelpers.assertDuration(result6, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, "date-time + offset + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00+04:15[America/Vancouver]";
+assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), "date-time + offset + IANA annotation throws if wall time and exact time mismatch");
diff --git a/test/built-ins/Temporal/Duration/prototype/add/relativeto-string-invalid.js b/test/built-ins/Temporal/Duration/prototype/add/relativeto-string-invalid.js
new file mode 100644
index 00000000000..bea9914cd45
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/relativeto-string-invalid.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: RangeError thrown if relativeTo is a string with the wrong format
+features: [Temporal]
+---*/
+
+['bad string', '15:30:45.123456', 'iso8601', 'UTC', 'P1YT1H'].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(1, 0, 0, 15);
+ assert.throws(RangeError, () => duration.add(new Temporal.Duration(0, 0, 0, 16), { relativeTo }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/add/relativeto-string-plaindatetime.js b/test/built-ins/Temporal/Duration/prototype/add/relativeto-string-plaindatetime.js
new file mode 100644
index 00000000000..5a806a826df
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/relativeto-string-plaindatetime.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: The relativeTo option accepts a PlainDateTime-like ISO 8601 string
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+['2000-01-01', '2000-01-01T00:00', '2000-01-01T00:00[u-ca=iso8601]'].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(1, 0, 0, 15);
+ const result = duration.add(new Temporal.Duration(0, 0, 0, 16), { relativeTo });
+ TemporalHelpers.assertDuration(result, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0);
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/add/relativeto-string-zoneddatetime.js b/test/built-ins/Temporal/Duration/prototype/add/relativeto-string-zoneddatetime.js
new file mode 100644
index 00000000000..0bc5351320d
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/relativeto-string-zoneddatetime.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: The relativeTo option accepts a ZonedDateTime-like ISO 8601 string
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ '2000-01-01[UTC]',
+ '2000-01-01T00:00[UTC]',
+ '2000-01-01T00:00+00:00[UTC]',
+ '2000-01-01T00:00+00:00[UTC][u-ca=iso8601]',
+].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(1, 0, 0, 15);
+ const result = duration.add(new Temporal.Duration(0, 0, 0, 16), { relativeTo });
+ TemporalHelpers.assertDuration(result, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0);
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-negative-epochnanoseconds.js b/test/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 00000000000..377a02df2a5
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const relativeTo = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+const duration = new Temporal.Duration(0, 0, 0, 1);
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time; in this
+// case via relativeTo.
+
+const result = duration.add(duration, { relativeTo });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0);
diff --git a/test/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..c7d5ad54714
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(2);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => duration.add(other, { relativeTo }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..e137b38e65f
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(2);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => duration.add(other, { relativeTo }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..6e86c2ffc77
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(2);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => duration.add(other, { relativeTo }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/add/subclassing-ignored.js b/test/built-ins/Temporal/Duration/prototype/add/subclassing-ignored.js
new file mode 100644
index 00000000000..db0c6d7e2ac
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.Duration,
+ [0, 0, 0, 4, 5, 6, 7, 987, 654, 321],
+ "add",
+ [{ nanoseconds: 1 }],
+ (result) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 5, 6, 7, 987, 654, 322),
+);
diff --git a/test/built-ins/Temporal/Duration/prototype/add/timezone-getpossibleinstantsfor-iterable.js b/test/built-ins/Temporal/Duration/prototype/add/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 00000000000..0443b1c47b3
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.duration.prototype.add steps 5–6:
+ 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ 6. Let _result_ be ? AddDuration(_duration_.[[Years]], [...], _duration_.[[Nanoseconds]], _other_.[[Years]], [...], _other_.[[Nanoseconds]], _relativeTo_).
+ sec-temporal-torelativetemporalobject step 6.d:
+ d. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNs_, _timeZone_, *"compatible"*, *"reject"*).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-addduration steps 7.d–e and 7.g.i:
+ d. Let _intermediateNs_ be ? AddZonedDateTime(_relativeTo_.[[Nanoseconds]], _timeZone_, _calendar_, _y1_, [...], _ns1_).
+ e. Let _endNs_ be ? AddZonedDateTime(_intermediateNs_, _timeZone_, _calendar_, _y2_, [...], _ns2_).
+ [...]
+ i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
+ sec-temporal-differencezoneddatetime step 8:
+ 8. Let _intermediateNs_ be ? AddZonedDateTime(_ns1_, _timeZone_, _calendar_, _dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], 0, 0, 0, 0, 0, 0, 0).
+ sec-temporal-addzoneddatetime step 8:
+ 8. Let _intermediateInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _intermediateDateTime_, *"compatible"*).
+ sec-temporal-builtintimezonegetinstantfor step 1:
+ 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "2000-01-01T09:00:00", // called once on the input relativeTo object
+ "2001-01-01T09:00:00", // called once on relativeTo plus the receiver
+ "2002-01-01T09:00:00", // called once on relativeTo plus the receiver plus the argument
+ "2002-01-01T09:00:00", // called once on relativeTo plus the years, months, and weeks from the difference of relativeTo minus endNs
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(0, 12);
+ duration1.add(duration2, { relativeTo: { year: 2000, month: 1, day: 1, hour: 9, timeZone } });
+}, expected);
diff --git a/test/built-ins/Temporal/Duration/prototype/add/timezone-string-datetime.js b/test/built-ins/Temporal/Duration/prototype/add/timezone-string-datetime.js
new file mode 100644
index 00000000000..a6cfc7dee66
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/add/timezone-string-datetime.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.add
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "bare date-time string is not a time zone");
+assert.throws(RangeError, () => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), "bare date-time string is not a time zone");
+
+// The following are all valid strings so should not throw:
+
+[
+ "2021-08-19T17:30Z",
+ "2021-08-19T17:30-07:00",
+ "2021-08-19T17:30[America/Vancouver]",
+ "2021-08-19T17:30Z[America/Vancouver]",
+ "2021-08-19T17:30-07:00[America/Vancouver]",
+].forEach((timeZone) => {
+ instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
+ instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } });
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/blank/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/blank/prop-desc.js
new file mode 100644
index 00000000000..f7ce58b0b59
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/blank/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.duration.prototype.blank
+description: The "blank" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "blank");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/Duration/prototype/days/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/days/prop-desc.js
new file mode 100644
index 00000000000..a482f9a75b7
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/days/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.duration.prototype.days
+description: The "days" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "days");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/Duration/prototype/hours/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/hours/prop-desc.js
new file mode 100644
index 00000000000..ef761fd69df
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/hours/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.duration.prototype.hours
+description: The "hours" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "hours");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/Duration/prototype/microseconds/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/microseconds/prop-desc.js
new file mode 100644
index 00000000000..3f860d4c010
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/microseconds/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.duration.prototype.microseconds
+description: The "microseconds" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "microseconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/Duration/prototype/milliseconds/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/milliseconds/prop-desc.js
new file mode 100644
index 00000000000..f7a56e343b2
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/milliseconds/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.duration.prototype.milliseconds
+description: The "milliseconds" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "milliseconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/Duration/prototype/minutes/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/minutes/prop-desc.js
new file mode 100644
index 00000000000..9a847ac7a44
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/minutes/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.duration.prototype.minutes
+description: The "minutes" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "minutes");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/Duration/prototype/months/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/months/prop-desc.js
new file mode 100644
index 00000000000..a55c5306744
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/months/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.duration.prototype.months
+description: The "months" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "months");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/Duration/prototype/nanoseconds/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/nanoseconds/prop-desc.js
new file mode 100644
index 00000000000..a74ebed2ad1
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/nanoseconds/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.duration.prototype.nanoseconds
+description: The "nanoseconds" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "nanoseconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/Duration/prototype/negated/builtin.js b/test/built-ins/Temporal/Duration/prototype/negated/builtin.js
new file mode 100644
index 00000000000..e9b20c0a6a5
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/negated/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.negated
+description: >
+ Tests that Temporal.Duration.prototype.negated
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Duration.prototype.negated),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.negated),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.negated),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.prototype.negated.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Duration/prototype/negated/length.js b/test/built-ins/Temporal/Duration/prototype/negated/length.js
new file mode 100644
index 00000000000..c43ebae3a4f
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/negated/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.negated
+description: Temporal.Duration.prototype.negated.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype.negated, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/negated/name.js b/test/built-ins/Temporal/Duration/prototype/negated/name.js
new file mode 100644
index 00000000000..8ce27654801
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/negated/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.negated
+description: Temporal.Duration.prototype.negated.name is "negated".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype.negated, "name", {
+ value: "negated",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/negated/not-a-constructor.js b/test/built-ins/Temporal/Duration/prototype/negated/not-a-constructor.js
new file mode 100644
index 00000000000..43b8e8be825
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/negated/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.negated
+description: >
+ Temporal.Duration.prototype.negated does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Duration.prototype.negated();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.prototype.negated), false,
+ "isConstructor(Temporal.Duration.prototype.negated)");
diff --git a/test/built-ins/Temporal/Duration/prototype/negated/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/negated/prop-desc.js
new file mode 100644
index 00000000000..f0349ad8e86
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/negated/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.negated
+description: The "negated" property of Temporal.Duration.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.prototype.negated,
+ "function",
+ "`typeof Duration.prototype.negated` is `function`"
+);
+
+verifyProperty(Temporal.Duration.prototype, "negated", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/negated/subclassing-ignored.js b/test/built-ins/Temporal/Duration/prototype/negated/subclassing-ignored.js
new file mode 100644
index 00000000000..9dcf121baa6
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/negated/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.negated
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.Duration,
+ [0, 0, 0, 4, 5, 6, 7, 987, 654, 321],
+ "negated",
+ [],
+ (result) => TemporalHelpers.assertDuration(result, 0, 0, 0, -4, -5, -6, -7, -987, -654, -321),
+);
diff --git a/test/built-ins/Temporal/Duration/prototype/round/balance-negative-result.js b/test/built-ins/Temporal/Duration/prototype/round/balance-negative-result.js
new file mode 100644
index 00000000000..5717d3ff77c
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/balance-negative-result.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: A negative duration result is balanced correctly by the modulo operation in NanosecondsToDays
+info: |
+ sec-temporal-nanosecondstodays step 6:
+ 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
+ sec-temporal-balanceduration step 4:
+ 4. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_).
+ sec-temporal.duration.prototype.round step 25:
+ 25. Let _result_ be ? BalanceDuration(_balanceResult_.[[Days]], _adjustResult_.[[Hours]], _adjustResult_.[[Minutes]], _adjustResult_.[[Seconds]], _adjustResult_.[[Milliseconds]], _adjustResult_.[[Microseconds]], _adjustResult_.[[Nanoseconds]], _largestUnit_, _relativeTo_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, -60);
+const result = duration.round({ largestUnit: "days" });
+TemporalHelpers.assertDuration(result, 0, 0, 0, -2, -12, 0, 0, 0, 0, 0);
diff --git a/test/built-ins/Temporal/Duration/prototype/round/builtin.js b/test/built-ins/Temporal/Duration/prototype/round/builtin.js
new file mode 100644
index 00000000000..179853ea0f7
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: >
+ Tests that Temporal.Duration.prototype.round
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Duration.prototype.round),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.round),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.round),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.prototype.round.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Duration/prototype/round/calendar-dateuntil-called-with-singular-largestunit.js b/test/built-ins/Temporal/Duration/prototype/round/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 00000000000..d0092fc8a3d
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,160 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.duration.prototype.round steps 20–25:
+ 20. Let _unbalanceResult_ be ? UnbalanceDurationRelative(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _largestUnit_, _relativeTo_).
+ 21. Let _roundResult_ be ? RoundDuration(_unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], _unbalanceResult_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_[[Seconds]], _duration_[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _relativeTo_).
+ 22. Let _adjustResult_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _relativeTo_).
+ 23. Let _balanceResult_ be ? BalanceDurationRelative(_adjustResult_.[[Years]], _adjustResult_.[[Months]], _adjustResult_.[[Weeks]], _adjustResult_.[[Days]], _largestUnit_, _relativeTo_).
+ 24. ...
+ 25. Let _result_ be ? BalanceDuration(_balanceResult_.[[Days]], _adjustResult_.[[Hours]], _adjustResult_.[[Minutes]], _adjustResult_.[[Seconds]], _adjustResult_.[[Milliseconds]], _adjustResult_.[[Microseconds]], _adjustResult.[[Nanoseconds]], _largestUnit_, _relativeTo_).
+ sec-temporal-unbalancedurationrelative steps 1 and 9.d.iii–v:
+ 1. If _largestUnit_ is *"year"*, or _years_, _months_, _weeks_, and _days_ are all 0, then
+ a. Return ...
+ ...
+ 9. If _largestUnit_ is *"month"*, then
+ ...
+ d. Repeat, while abs(_years_) > 0,
+ ...
+ iii. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
+ iv. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*).
+ v. Let _untilResult_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _newRelativeTo_, _untilOptions_, _dateUntil_).
+ sec-temporal-roundduration steps 5.d and 8.n–p:
+ 5. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
+ ...
+ 8. If _unit_ is *"year"*, then
+ ...
+ n. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
+ o. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"year"*).
+ p. Let _timePassed_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _daysLater_, _untilOptions_)
+ sec-temporal-adjustroundeddurationdays steps 1 and 9:
+ 1. If _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot; or _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*; or _unit_ is *"nanosecond"* and _increment_ is 1, then
+ a. Return ...
+ ...
+ 9. Let _adjustedDateDuration_ be ? AddDuration(_years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0, 0, 0, 0, _direction_, 0, 0, 0, 0, 0, 0, _relativeTo_).
+ sec-temporal-addduration step 7.a–g:
+ a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot.
+ ...
+ f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ g. Else,
+ i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
+ sec-temporal-balancedurationrelative steps 1, 9.m–o, and 9.q.vi–viii:
+ 1. If _largestUnit_ is not one of *"year"*, *"month"*, or *"week"*, or _years_, _months_, _weeks_, and _days_ are all 0, then
+ a. Return ...
+ ...
+ 9. If _largestUnit_ is *"year"*, then
+ ...
+ m. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
+ n. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*).
+ o. Let _untilResult_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _newRelativeTo_, _untilOptions_, _dateUntil_).
+ p. ...
+ q. Repeat, while abs(_months_) ≥ abs(_oneYearMonths_),
+ ...
+ vi. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
+ vii. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*).
+ viii. Let _untilResult_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _newRelativeTo_, _untilOptions_, _dateUntil_).
+ sec-temporal-balanceduration step 3.a:
+ 3. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_).
+ sec-temporal-differencezoneddatetime steps 7 and 11:
+ 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_).
+ 11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_).
+ sec-temporal-nanosecondstodays step 11:
+ 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*).
+ sec-temporal-differenceisodatetime steps 9–11:
+ 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
+ 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
+ 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Check with smallestUnit nanoseconds but roundingIncrement > 1; each call
+// should result in two calls to dateUntil() originating from
+// AdjustRoundedDurationDays, one with largestUnit equal to the largest unit in
+// the duration higher than "day", and one with largestUnit: "day".
+// Additionally one call with largestUnit: "month" in BalanceDurationRelative
+// when the largestUnit given to round() is "year", and one call with
+// largestUnit: "day" when the largestUnit given to round() is "year", "month",
+// "week", or "day".
+
+const durations = [
+ [1, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999],
+ [0, 1, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999],
+ [0, 0, 1, 0, 0, 0, 0, 0, 0, 86399_999_999_999],
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999],
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999],
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999],
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999],
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999],
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999],
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999],
+].map((args) => new Temporal.Duration(...args));
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit, index) => {
+ const duration = durations[index];
+ const relativeTo = new Temporal.ZonedDateTime(0n, "UTC", calendar);
+ duration.round({ largestUnit, roundingIncrement: 2, roundingMode: 'ceil', relativeTo });
+ },
+ {
+ years: ["year", "day", "month", "day"],
+ months: ["month", "day", "day"],
+ weeks: ["week", "day", "day"],
+ days: ["day", "day", "day"],
+ hours: ["day", "day"],
+ minutes: ["day", "day"],
+ seconds: ["day", "day"],
+ milliseconds: ["day", "day"],
+ microseconds: ["day", "day"],
+ nanoseconds: ["day", "day"]
+ }
+);
+
+// Check the path that converts months to years and vice versa in
+// BalanceDurationRelative and UnbalanceDurationRelative.
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const duration = new Temporal.Duration(5, 60);
+ const relativeTo = new Temporal.PlainDateTime(2000, 5, 2, 0, 0, 0, 0, 0, 0, calendar);
+ duration.round({ largestUnit, relativeTo });
+ },
+ {
+ years: ["month", "month", "month", "month", "month", "month"],
+ months: ["month", "month", "month", "month", "month"],
+ weeks: [],
+ days: [],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+// Check the paths that call dateUntil() in RoundDuration. These paths do not
+// call dateUntil() in AdjustRoundedDurationDays. Note that there is no
+// largestUnit: "month" call in BalanceDurationRelative and no largestUnit:
+// "day" call in BalanceDuration, because the durations have rounded down to 0.
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const duration = new Temporal.Duration(0, 0, 0, 0, 1, 1, 1, 1, 1, 1);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+ duration.round({ largestUnit, smallestUnit: largestUnit, relativeTo });
+ }, {
+ years: ["day", "year"],
+ months: ["day"],
+ weeks: ["day"],
+ days: ["day"]
+ }
+);
diff --git a/test/built-ins/Temporal/Duration/prototype/round/calendar-fields-iterable.js b/test/built-ins/Temporal/Duration/prototype/round/calendar-fields-iterable.js
new file mode 100644
index 00000000000..3703596d710
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/calendar-fields-iterable.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.duration.prototype.round step 19:
+ 19. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1);
+duration.round({ smallestUnit: 'months', relativeTo: { year: 2000, month: 1, day: 1, calendar } });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/Duration/prototype/round/calendar-temporal-object.js b/test/built-ins/Temporal/Duration/prototype/round/calendar-temporal-object.js
new file mode 100644
index 00000000000..01c36f73aaf
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.duration.prototype.round step 19:
+ 19. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1);
+ duration.round({ smallestUnit: 'months', relativeTo: { year: 2000, month: 1, day: 1, calendar: temporalObject } });
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/round/dateuntil-field.js b/test/built-ins/Temporal/Duration/prototype/round/dateuntil-field.js
new file mode 100644
index 00000000000..05c73abff77
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/dateuntil-field.js
@@ -0,0 +1,45 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: >
+ When consulting calendar.dateUntil() to calculate the number of months in a
+ year, the months property is not accessed on the result Duration
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// One path, through UnbalanceDurationRelative, calls dateUntil() in a loop for
+// each year in the duration
+
+const actual = [];
+const expected1 = [
+ "call dateUntil",
+ "call dateUntil",
+];
+const duration = new Temporal.Duration(0, 12);
+TemporalHelpers.observeProperty(actual, duration, "months", 1);
+
+const calendar = TemporalHelpers.calendarDateUntilObservable(actual, duration);
+const relativeTo = new Temporal.PlainDateTime(2018, 10, 12, 0, 0, 0, 0, 0, 0, calendar);
+
+const years = new Temporal.Duration(2);
+const result1 = years.round({ largestUnit: "months", relativeTo });
+TemporalHelpers.assertDuration(result1, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, "result");
+assert.compareArray(actual, expected1, "operations");
+
+// There is a second path, through BalanceDurationRelative, that calls
+// dateUntil() in a loop for each year in the duration plus one extra time
+
+actual.splice(0, actual.length); // reset calls for next test
+const expected2 = [
+ "call dateUntil",
+ "call dateUntil",
+ "call dateUntil",
+];
+
+const months = new Temporal.Duration(0, 24);
+const result2 = months.round({ largestUnit: "years", relativeTo });
+TemporalHelpers.assertDuration(result2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "result");
+assert.compareArray(actual, expected2, "operations");
diff --git a/test/built-ins/Temporal/Duration/prototype/round/largestunit-invalid-string.js b/test/built-ins/Temporal/Duration/prototype/round/largestunit-invalid-string.js
new file mode 100644
index 00000000000..9c5cacc1ae7
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/largestunit-invalid-string.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+assert.throws(RangeError, () => duration.round({ largestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/Duration/prototype/round/largestunit-plurals-accepted.js b/test/built-ins/Temporal/Duration/prototype/round/largestunit-plurals-accepted.js
new file mode 100644
index 00000000000..e0b884a5a65
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/largestunit-plurals-accepted.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const relativeTo = new Temporal.PlainDate(2000, 1, 1);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => duration.round({ largestUnit, relativeTo }), validUnits);
diff --git a/test/built-ins/Temporal/Duration/prototype/round/largestunit-undefined.js b/test/built-ins/Temporal/Duration/prototype/round/largestunit-undefined.js
new file mode 100644
index 00000000000..92dc48b611a
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/largestunit-undefined.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration1 = new Temporal.Duration(0, 0, 0, 0, 1, 120, 1, 123, 456, 789);
+const explicit1 = duration1.round({ largestUnit: undefined, smallestUnit: "nanosecond" });
+TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 0, 3, 0, 1, 123, 456, 789, "default largestUnit is largest in input");
+const implicit1 = duration1.round({ smallestUnit: "nanosecond" });
+TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 0, 3, 0, 1, 123, 456, 789, "default largestUnit is largest in input");
+
+const duration2 = new Temporal.Duration(0, 0, 0, 0, 0, 120, 1, 123, 456, 789);
+const explicit2 = duration2.round({ largestUnit: undefined, smallestUnit: "nanosecond" });
+TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 0, 0, 120, 1, 123, 456, 789, "default largestUnit is largest in input");
+const implicit2 = duration2.round({ smallestUnit: "nanosecond" });
+TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 0, 0, 120, 1, 123, 456, 789, "default largestUnit is largest in input");
diff --git a/test/built-ins/Temporal/Duration/prototype/round/largestunit-wrong-type.js b/test/built-ins/Temporal/Duration/prototype/round/largestunit-wrong-type.js
new file mode 100644
index 00000000000..8e35abdc342
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/largestunit-wrong-type.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 456, 789);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "minute",
+ (largestUnit) => duration.round({ largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 754, 56, 123, 456, 789, descr),
+);
diff --git a/test/built-ins/Temporal/Duration/prototype/round/length.js b/test/built-ins/Temporal/Duration/prototype/round/length.js
new file mode 100644
index 00000000000..42e7d557fe4
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: Temporal.Duration.prototype.round.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype.round, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/round/name.js b/test/built-ins/Temporal/Duration/prototype/round/name.js
new file mode 100644
index 00000000000..1a41fb5ee0c
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: Temporal.Duration.prototype.round.name is "round".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype.round, "name", {
+ value: "round",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/round/not-a-constructor.js b/test/built-ins/Temporal/Duration/prototype/round/not-a-constructor.js
new file mode 100644
index 00000000000..cc8e0ba8131
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: >
+ Temporal.Duration.prototype.round does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Duration.prototype.round();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.prototype.round), false,
+ "isConstructor(Temporal.Duration.prototype.round)");
diff --git a/test/built-ins/Temporal/Duration/prototype/round/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/round/prop-desc.js
new file mode 100644
index 00000000000..f360a589f6f
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: The "round" property of Temporal.Duration.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.prototype.round,
+ "function",
+ "`typeof Duration.prototype.round` is `function`"
+);
+
+verifyProperty(Temporal.Duration.prototype, "round", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/round/read-time-fields-before-datefromfields.js b/test/built-ins/Temporal/Duration/prototype/round/read-time-fields-before-datefromfields.js
new file mode 100644
index 00000000000..e5c67cb3865
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/read-time-fields-before-datefromfields.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.duration.prototype.round step 19:
+ 19. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.g:
+ g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInvalidGettersTime();
+const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1);
+duration.round({ smallestUnit: 'months', relativeTo: { year: 2000, month: 1, day: 1, calendar } });
diff --git a/test/built-ins/Temporal/Duration/prototype/round/relativeto-infinity-throws-rangeerror.js b/test/built-ins/Temporal/Duration/prototype/round/relativeto-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..d411662a905
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/relativeto-infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.duration.prototype.round
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.round({ smallestUnit: "seconds", relativeTo: { ...base, [prop]: inf } }), `${prop} property cannot be ${inf} in relativeTo`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.round({ smallestUnit: "seconds", relativeTo: { ...base, [prop]: obj } }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..d9d531dd244
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ assert.throws(RangeError, () => duration.round({ smallestUnit: "seconds", relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..fc22ce61127
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ assert.throws(RangeError, () => duration.round({ smallestUnit: "seconds", relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..0e5ba948ba1
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ assert.throws(TypeError, () => duration.round({ smallestUnit: "seconds", relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/round/relativeto-string-datetime.js b/test/built-ins/Temporal/Duration/prototype/round/relativeto-string-datetime.js
new file mode 100644
index 00000000000..cab748e6686
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/relativeto-string-datetime.js
@@ -0,0 +1,40 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: >
+ Conversion of ISO date-time strings as relativeTo option to
+ Temporal.ZonedDateTime or Temporal.PlainDateTime instances
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+let relativeTo = "2019-11-01T00:00";
+const result1 = instance.round({ largestUnit: "years", relativeTo });
+TemporalHelpers.assertDuration(result1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, "bare date-time string is a plain relativeTo");
+
+relativeTo = "2019-11-01T00:00Z";
+const result2 = instance.round({ largestUnit: "years", relativeTo });
+TemporalHelpers.assertDuration(result2, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, "date-time + Z is a plain relativeTo");
+
+relativeTo = "2019-11-01T00:00-07:00";
+const result3 = instance.round({ largestUnit: "years", relativeTo });
+TemporalHelpers.assertDuration(result3, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, "date-time + offset is a plain relativeTo");
+
+relativeTo = "2019-11-01T00:00[America/Vancouver]";
+const result4 = instance.round({ largestUnit: "years", relativeTo });
+TemporalHelpers.assertDuration(result4, 1, 0, 0, 0, 24, 0, 0, 0, 0, 0, "date-time + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00Z[America/Vancouver]";
+const result5 = instance.round({ largestUnit: "years", relativeTo });
+TemporalHelpers.assertDuration(result5, 1, 0, 0, 0, 24, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00-07:00[America/Vancouver]";
+const result6 = instance.round({ largestUnit: "years", relativeTo });
+TemporalHelpers.assertDuration(result6, 1, 0, 0, 0, 24, 0, 0, 0, 0, 0, "date-time + offset + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00+04:15[America/Vancouver]";
+assert.throws(RangeError, () => instance.round({ largestUnit: "years", relativeTo }), "date-time + offset + IANA annotation throws if wall time and exact time mismatch");
diff --git a/test/built-ins/Temporal/Duration/prototype/round/relativeto-string-invalid.js b/test/built-ins/Temporal/Duration/prototype/round/relativeto-string-invalid.js
new file mode 100644
index 00000000000..3b76b0fbc6e
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/relativeto-string-invalid.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: RangeError thrown if relativeTo is a string with the wrong format
+features: [Temporal]
+---*/
+
+['bad string', '15:30:45.123456', 'iso8601', 'UTC', 'P1YT1H'].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(0, 0, 0, 31);
+ assert.throws(RangeError, () => duration.round({ largestUnit: "months", relativeTo }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/round/relativeto-string-plaindatetime.js b/test/built-ins/Temporal/Duration/prototype/round/relativeto-string-plaindatetime.js
new file mode 100644
index 00000000000..8b505cecb4d
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/relativeto-string-plaindatetime.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: The relativeTo option accepts a PlainDateTime-like ISO 8601 string
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+['2000-01-01', '2000-01-01T00:00', '2000-01-01T00:00[u-ca=iso8601]'].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(0, 0, 0, 31);
+ const result = duration.round({ largestUnit: "months", relativeTo });
+ TemporalHelpers.assertDuration(result, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0);
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/round/relativeto-string-zoneddatetime.js b/test/built-ins/Temporal/Duration/prototype/round/relativeto-string-zoneddatetime.js
new file mode 100644
index 00000000000..dffdd288afc
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/relativeto-string-zoneddatetime.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: The relativeTo option accepts a ZonedDateTime-like ISO 8601 string
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ '2000-01-01[UTC]',
+ '2000-01-01T00:00[UTC]',
+ '2000-01-01T00:00+00:00[UTC]',
+ '2000-01-01T00:00+00:00[UTC][u-ca=iso8601]',
+].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(0, 0, 0, 31);
+ const result = duration.round({ largestUnit: "months", relativeTo });
+ TemporalHelpers.assertDuration(result, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0);
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/round/relativeto-undefined-throw-on-calendar-units.js b/test/built-ins/Temporal/Duration/prototype/round/relativeto-undefined-throw-on-calendar-units.js
new file mode 100644
index 00000000000..fc4d613a7cc
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/relativeto-undefined-throw-on-calendar-units.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: >
+ The relativeTo option is required when the Duration contains years, months,
+ or weeks, and largestUnit is days; or largestUnit is weeks or months
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const oneYear = new Temporal.Duration(1);
+const oneMonth = new Temporal.Duration(0, 1);
+const oneWeek = new Temporal.Duration(0, 0, 1);
+const oneDay = new Temporal.Duration(0, 0, 0, 1);
+
+const options = { largestUnit: "days" };
+TemporalHelpers.assertDuration(oneDay.round(options), 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, "days do not require relativeTo");
+assert.throws(RangeError, () => oneWeek.round(options), "balancing weeks to days requires relativeTo");
+assert.throws(RangeError, () => oneMonth.round(options), "balancing months to days requires relativeTo");
+assert.throws(RangeError, () => oneYear.round(options), "balancing years to days requires relativeTo");
+
+["months", "weeks"].forEach((largestUnit) => {
+ [oneDay, oneWeek, oneMonth, oneYear].forEach((duration) => {
+ assert.throws(RangeError, () => duration.round({ largestUnit }), `balancing ${duration} to ${largestUnit} requires relativeTo`);
+ });
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-negative-epochnanoseconds.js b/test/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 00000000000..d1f0b4f36eb
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const relativeTo = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+const duration = new Temporal.Duration(0, 0, 0, 1);
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time; in this
+// case via relativeTo.
+
+const result = duration.round({ relativeTo, largestUnit: "days" });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0);
diff --git a/test/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..35e0abbba7a
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => duration.round({ smallestUnit: "seconds", relativeTo: datetime }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..9886aef06e0
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => duration.round({ smallestUnit: "seconds", relativeTo: datetime }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..f6642348d36
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => duration.round({ smallestUnit: "seconds", relativeTo: datetime }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/round/round-negative-result.js b/test/built-ins/Temporal/Duration/prototype/round/round-negative-result.js
new file mode 100644
index 00000000000..4b5f28b6e34
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/round-negative-result.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: A negative duration result is balanced correctly by the modulo operation in NanosecondsToDays
+info: |
+ sec-temporal-nanosecondstodays step 6:
+ 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Return the new Record { ..., [[Nanoseconds]]\: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
+ sec-temporal-roundduration step 6:
+ 6. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
+ sec-temporal.duration.prototype.round step 21:
+ 21. Let _roundResult_ be ? RoundDuration(_unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], _unbalanceResult_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_[[Seconds]], _duration_[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _relativeTo_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, -60);
+const result = duration.round({ smallestUnit: "days" });
+TemporalHelpers.assertDuration(result, 0, 0, 0, -3, 0, 0, 0, 0, 0, 0);
diff --git a/test/built-ins/Temporal/Duration/prototype/round/roundingincrement-nan.js b/test/built-ins/Temporal/Duration/prototype/round/roundingincrement-nan.js
new file mode 100644
index 00000000000..c94f9fcf9b0
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/roundingincrement-nan.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.duration.prototype.round step 18:
+ 18. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 4, 12, 34, 56, 987, 654, 321);
+assert.throws(RangeError, () => duration.round({ smallestUnit: 'second', roundingIncrement: NaN }));
diff --git a/test/built-ins/Temporal/Duration/prototype/round/roundingincrement-undefined.js b/test/built-ins/Temporal/Duration/prototype/round/roundingincrement-undefined.js
new file mode 100644
index 00000000000..5fb20169a84
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/roundingincrement-undefined.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.duration.prototype.round step 18:
+ 18. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 4, 12, 34, 56, 987, 654, 321);
+
+const explicit = duration.round({ smallestUnit: 'second', roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 4, 12, 34, 57, 0, 0, 0, "default roundingIncrement is 1");
+
+const implicit = duration.round({ smallestUnit: 'second' });
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 4, 12, 34, 57, 0, 0, 0, "default roundingIncrement is 1");
diff --git a/test/built-ins/Temporal/Duration/prototype/round/roundingincrement-wrong-type.js b/test/built-ins/Temporal/Duration/prototype/round/roundingincrement-wrong-type.js
new file mode 100644
index 00000000000..3f3e285ecb1
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/roundingincrement-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaintime.prototype.round step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 4, 12, 34, 56, 987, 654, 321);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => duration.round({ smallestUnit: 'second', roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 12, 34, 57, 0, 0, 0, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 12, 34, 56, 0, 0, 0, descr),
+);
diff --git a/test/built-ins/Temporal/Duration/prototype/round/roundingmode-invalid-string.js b/test/built-ins/Temporal/Duration/prototype/round/roundingmode-invalid-string.js
new file mode 100644
index 00000000000..35a9df28076
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/roundingmode-invalid-string.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+assert.throws(RangeError, () => duration.round({ smallestUnit: "microsecond", roundingMode: "other string" }));
diff --git a/test/built-ins/Temporal/Duration/prototype/round/roundingmode-undefined.js b/test/built-ins/Temporal/Duration/prototype/round/roundingmode-undefined.js
new file mode 100644
index 00000000000..1acab9a42eb
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/roundingmode-undefined.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+
+const explicit1 = duration.round({ smallestUnit: "microsecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 0, 12, 34, 56, 123, 988, 0, "default roundingMode is halfExpand");
+const implicit1 = duration.round({ smallestUnit: "microsecond" });
+TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 0, 12, 34, 56, 123, 988, 0, "default roundingMode is halfExpand");
+
+const explicit2 = duration.round({ smallestUnit: "millisecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 0, 12, 34, 56, 124, 0, 0, "default roundingMode is halfExpand");
+const implicit2 = duration.round({ smallestUnit: "millisecond" });
+TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 0, 12, 34, 56, 124, 0, 0, "default roundingMode is halfExpand");
+
+const explicit3 = duration.round({ smallestUnit: "second", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit3, 0, 0, 0, 0, 12, 34, 56, 0, 0, 0, "default roundingMode is halfExpand");
+const implicit3 = duration.round({ smallestUnit: "second" });
+TemporalHelpers.assertDuration(implicit3, 0, 0, 0, 0, 12, 34, 56, 0, 0, 0, "default roundingMode is halfExpand");
diff --git a/test/built-ins/Temporal/Duration/prototype/round/roundingmode-wrong-type.js b/test/built-ins/Temporal/Duration/prototype/round/roundingmode-wrong-type.js
new file mode 100644
index 00000000000..e1305598e96
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/roundingmode-wrong-type.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "halfExpand",
+ (roundingMode) => duration.round({ smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 12, 34, 56, 123, 988, 0, descr),
+);
diff --git a/test/built-ins/Temporal/Duration/prototype/round/smallestunit-invalid-string.js b/test/built-ins/Temporal/Duration/prototype/round/smallestunit-invalid-string.js
new file mode 100644
index 00000000000..3d5d884b527
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/smallestunit-invalid-string.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+assert.throws(RangeError, () => duration.round({ smallestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/Duration/prototype/round/smallestunit-plurals-accepted.js b/test/built-ins/Temporal/Duration/prototype/round/smallestunit-plurals-accepted.js
new file mode 100644
index 00000000000..eee32e8faa0
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/smallestunit-plurals-accepted.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const relativeTo = new Temporal.PlainDate(2000, 1, 1);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => duration.round({ smallestUnit, relativeTo }), validUnits);
diff --git a/test/built-ins/Temporal/Duration/prototype/round/smallestunit-undefined.js b/test/built-ins/Temporal/Duration/prototype/round/smallestunit-undefined.js
new file mode 100644
index 00000000000..7a25f7c1b06
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/smallestunit-undefined.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 1, 2, 3, 123, 456, 789);
+const explicit1 = duration.round({ largestUnit: "day", smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 0, 1, 2, 3, 123, 456, 789, "default smallestUnit is nanosecond");
+const implicit1 = duration.round({ largestUnit: "day" });
+TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 0, 1, 2, 3, 123, 456, 789, "default smallestUnit is nanosecond");
diff --git a/test/built-ins/Temporal/Duration/prototype/round/smallestunit-wrong-type.js b/test/built-ins/Temporal/Duration/prototype/round/smallestunit-wrong-type.js
new file mode 100644
index 00000000000..c177013a880
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/smallestunit-wrong-type.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => duration.round({ smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 12, 34, 56, 123, 988, 0, descr),
+);
diff --git a/test/built-ins/Temporal/Duration/prototype/round/subclassing-ignored.js b/test/built-ins/Temporal/Duration/prototype/round/subclassing-ignored.js
new file mode 100644
index 00000000000..163178afb56
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.Duration,
+ [0, 0, 0, 4, 5, 6, 7, 987, 654, 321],
+ "round",
+ [{ smallestUnit: 'seconds' }],
+ (result) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 5, 6, 8, 0, 0, 0),
+);
diff --git a/test/built-ins/Temporal/Duration/prototype/round/timezone-getpossibleinstantsfor-iterable.js b/test/built-ins/Temporal/Duration/prototype/round/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 00000000000..a67eb7e60a5
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,45 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.duration.prototype.round steps 19, 21, and 24:
+ 19. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ 21. Let _roundResult_ be ? RoundDuration(_unbalanceResult_.[[Years]], [...], _unbalanceResult_.[[Days]], _duration_.[[Hours]], [...], _duration_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _relativeTo_).
+ 24. If _relativeTo_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Set _relativeTo_ to ? MoveRelativeZonedDateTime(_relativeTo_, _balanceResult_.[[Years]], _balanceResult_.[[Months]], _balanceResult_.[[Weeks]], 0).
+ sec-temporal-torelativetemporalobject step 6.d:
+ d. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNs_, _timeZone_, *"compatible"*, *"reject"*).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-roundduration step 5.c–d:
+ c. If _zonedRelativeTo_ is not *undefined*, then
+ i. Let _intermediate_ be ? MoveRelativeZonedDateTime(_zonedRelativeTo_, _years_, _months_, _weeks_, _days_).
+ d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
+ sec-temporal-moverelativezoneddatetime step 1:
+ 1. Let _intermediateNs_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]], _years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0).
+ sec-temporal-nanosecondstodays step 13:
+ 13. Let _intermediateNs_ be ? AddZonedDateTime(_startNs_, _relativeTo_.[[TimeZone]], _relativeTo_.[[Calendar]], 0, 0, 0, _days_, 0, 0, 0, 0, 0, 0).
+ sec-temporal-addzoneddatetime step 8:
+ 8. Let _intermediateInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _intermediateDateTime_, *"compatible"*).
+ sec-temporal-builtintimezonegetinstantfor step 1:
+ 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "2000-01-01T00:00:00", // called once on the input relativeTo object
+ "2001-02-09T00:00:00", // called once on relativeTo plus years, months, weeks, days from the receiver
+ "2001-02-10T00:00:00", // called once on the previous value plus the calendar days difference between that and the time part of the duration
+ "2001-02-01T00:00:00", // called once on relativeTo plus the years, months, and weeks part of the balance result
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1);
+ duration.round({ smallestUnit: 'months', relativeTo: { year: 2000, month: 1, day: 1, timeZone } });
+}, expected);
diff --git a/test/built-ins/Temporal/Duration/prototype/round/timezone-string-datetime.js b/test/built-ins/Temporal/Duration/prototype/round/timezone-string-datetime.js
new file mode 100644
index 00000000000..27058b8c7c6
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/round/timezone-string-datetime.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.round
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "bare date-time string is not a time zone");
+assert.throws(RangeError, () => instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), "bare date-time string is not a time zone");
+
+// The following are all valid strings so should not throw:
+
+[
+ "2021-08-19T17:30Z",
+ "2021-08-19T17:30-07:00",
+ "2021-08-19T17:30[America/Vancouver]",
+ "2021-08-19T17:30Z[America/Vancouver]",
+ "2021-08-19T17:30-07:00[America/Vancouver]",
+].forEach((timeZone) => {
+ instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
+ instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } });
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/seconds/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/seconds/prop-desc.js
new file mode 100644
index 00000000000..ea7f03b9bb8
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/seconds/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.duration.prototype.seconds
+description: The "seconds" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "seconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/Duration/prototype/sign/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/sign/prop-desc.js
new file mode 100644
index 00000000000..0f72c33ff76
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/sign/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.duration.prototype.sign
+description: The "sign" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "sign");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/argument-string-negative-fractional-units.js b/test/built-ins/Temporal/Duration/prototype/subtract/argument-string-negative-fractional-units.js
new file mode 100644
index 00000000000..3c940f26d3a
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/argument-string-negative-fractional-units.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration();
+
+const resultHours = instance.subtract("-PT24.567890123H");
+TemporalHelpers.assertDuration(resultHours, 0, 0, 0, 0, 24, 34, 4, 404, 442, 799, "negative fractional hours");
+
+const resultMinutes = instance.subtract("-PT1440.567890123M");
+TemporalHelpers.assertDuration(resultMinutes, 0, 0, 0, 0, 0, 1440, 34, 73, 407, 379, "negative fractional minutes");
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/balance-negative-result.js b/test/built-ins/Temporal/Duration/prototype/subtract/balance-negative-result.js
new file mode 100644
index 00000000000..3f393551325
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/balance-negative-result.js
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: A negative duration result is balanced correctly by the modulo operation in NanosecondsToDays
+info: |
+ sec-temporal-nanosecondstodays step 6:
+ 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
+ sec-temporal-balanceduration step 4:
+ 4. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_).
+ sec-temporal-addduration steps 5–6:
+ 5. If _relativeTo_ is *undefined*, then
+ ...
+ b. Let _result_ be ! BalanceDuration(_d1_ + _d2_, _h1_ + _h2_, _min1_ + _min2_, _s1_ + _s2_, _ms1_ + _ms2_, _mus1_ + _mus2_, _ns1_ + _ns2_, _largestUnit_).
+ ...
+ 6. Else if _relativeTo_ has an [[InitializedTemporalPlainDateTime]] internal slot, then
+ ...
+ n. Let _result_ be ! BalanceDuration(_dateDifference_.[[Days]], _h1_ + _h2_, _min1_ + _min2_, _s1_ + _s2_, _ms1_ + _ms2_, _mus1_ + _mus2_, _ns1_ + _ns2_, _largestUnit_).
+ sec-temporal.duration.prototype.subtract step 6:
+ 6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], −_other_.[[Years]], −_other_.[[Months]], −_other_.[[Weeks]], −_other_.[[Days]], −_other_.[[Hours]], −_other_.[[Minutes]], −_other_.[[Seconds]], −_other_.[[Milliseconds]], −_other_.[[Microseconds]], −_other_.[[Nanoseconds]], _relativeTo_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration1 = new Temporal.Duration(0, 0, 0, 0, -60);
+const duration2 = new Temporal.Duration(0, 0, 0, -1);
+
+const resultNotRelative = duration1.subtract(duration2);
+TemporalHelpers.assertDuration(resultNotRelative, 0, 0, 0, -1, -12, 0, 0, 0, 0, 0);
+
+const relativeTo = new Temporal.PlainDateTime(2000, 1, 1);
+const resultRelative = duration1.subtract(duration2, { relativeTo });
+TemporalHelpers.assertDuration(resultRelative, 0, 0, 0, -1, -12, 0, 0, 0, 0, 0);
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/balance-negative-time-units.js b/test/built-ins/Temporal/Duration/prototype/subtract/balance-negative-time-units.js
new file mode 100644
index 00000000000..81a022ec9ed
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/balance-negative-time-units.js
@@ -0,0 +1,60 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: Negative time fields in relativeTo are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-differencetime step 8:
+ 8. Let _bt_ be ? BalanceTime(_hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal-differenceisodatetime step 2:
+ 2. Let _timeDifference_ be ? DifferenceTime(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_).
+ sec-temporal-differencezoneddatetime step 7:
+ 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_).
+ sec-temporal-addduration step 7.g.i:
+ i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
+ sec-temporal.duration.prototype.subtract step 6:
+ 6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], −_other_.[[Years]], −_other_.[[Months]], −_other_.[[Weeks]], −_other_.[[Days]], −_other_.[[Hours]], −_other_.[[Minutes]], −_other_.[[Seconds]], −_other_.[[Milliseconds]], −_other_.[[Microseconds]], −_other_.[[Nanoseconds]], _relativeTo_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 1, 1, 1, 1, 1, 1);
+
+const timeZone = new Temporal.TimeZone("UTC");
+const relativeTo = new Temporal.ZonedDateTime(830998861_000_000_000n, timeZone);
+// This code path is encountered if largestUnit is years, months, weeks, or days
+// and relativeTo is a ZonedDateTime
+const options = { largestUnit: "days", relativeTo };
+
+const result1 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 2), options);
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 2), options);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 2), options);
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 2), options);
+TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 2), options);
+TemporalHelpers.assertDuration(result5, 0, 0, 0, 0, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+// This one is different because hours are later balanced again in BalanceDuration
+const result6 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 2), options);
+TemporalHelpers.assertDuration(result6, 0, 0, 0, 0, 0, -58, -58, -998, -998, -999, "hours balance");
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/builtin.js b/test/built-ins/Temporal/Duration/prototype/subtract/builtin.js
new file mode 100644
index 00000000000..8f1811c8208
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: >
+ Tests that Temporal.Duration.prototype.subtract
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Duration.prototype.subtract),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.subtract),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.subtract),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.prototype.subtract.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd-called-with-options-undefined.js b/test/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd-called-with-options-undefined.js
new file mode 100644
index 00000000000..52d2e40a118
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd-called-with-options-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: >
+ BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the
+ options value
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddUndefinedOptions();
+const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9);
+const instance = new Temporal.Duration(1, 1, 1, 1);
+instance.subtract(new Temporal.Duration(-1, -1, -1, -1), { relativeTo: new Temporal.ZonedDateTime(0n, timeZone, calendar) });
+assert.sameValue(calendar.dateAddCallCount, 5);
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/calendar-dateuntil-called-with-singular-largestunit.js b/test/built-ins/Temporal/Duration/prototype/subtract/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 00000000000..ef5fbbfedb3
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,78 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.duration.prototype.subtract step 6:
+ 6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], −_other_.[[Years]], −_other_.[[Months]], −_other_.[[Weeks]], −_other_.[[Days]], −_other_.[[Hours]], −_other_.[[Minutes]], −_other_.[[Seconds]], −_other_.[[Milliseconds]], −_other_.[[Microseconds]], −_other_.[[Nanoseconds]], _relativeTo_).
+ sec-temporal-addduration steps 6-7:
+ 6. If _relativeTo_ has an [[InitializedTemporalPlainDateTime]] internal slot, then
+ ...
+ j. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
+ k. Let _differenceOptions_ be ! OrdinaryObjectCreate(*null*).
+ l. Perform ! CreateDataPropertyOrThrow(_differenceOptions_, *"largestUnit"*, _dateLargestUnit_).
+ m. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _datePart_, _end_, _differenceOptions_).
+ ...
+ 7. Else,
+ a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot.
+ ...
+ f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ g. Else,
+ i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
+ sec-temporal-differencezoneddatetime steps 7 and 11:
+ 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_).
+ 11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_).
+ sec-temporal-nanosecondstodays step 11:
+ 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*).
+ sec-temporal-differenceisodatetime steps 9–11:
+ 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
+ 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
+ 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit, index) => {
+ const one = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(1)]);
+ const two = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(2)]);
+ const relativeTo = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+ two.subtract(one, { relativeTo });
+ },
+ {
+ years: ["year"],
+ months: ["month"],
+ weeks: ["week"],
+ days: ["day"],
+ hours: ["day"],
+ minutes: ["day"],
+ seconds: ["day"],
+ milliseconds: ["day"],
+ microseconds: ["day"],
+ nanoseconds: ["day"]
+ }
+);
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit, index) => {
+ const one = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(1)]);
+ const two = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(2)]);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+ two.subtract(one, { relativeTo });
+ },
+ {
+ years: ["year", "day"],
+ months: ["month", "day"],
+ weeks: ["week", "day"],
+ days: ["day", "day"],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/calendar-fields-iterable.js b/test/built-ins/Temporal/Duration/prototype/subtract/calendar-fields-iterable.js
new file mode 100644
index 00000000000..b9d887416ff
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/calendar-fields-iterable.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.duration.prototype.subtract step 5:
+ 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const duration1 = new Temporal.Duration(1);
+const duration2 = new Temporal.Duration(0, 12);
+duration1.subtract(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar } });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/calendar-temporal-object.js b/test/built-ins/Temporal/Duration/prototype/subtract/calendar-temporal-object.js
new file mode 100644
index 00000000000..c870c1690fc
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/calendar-temporal-object.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.duration.prototype.subtract step 5:
+ 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(0, 12);
+ duration1.subtract(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar: temporalObject } });
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/infinity-throws-rangeerror.js b/test/built-ins/Temporal/Duration/prototype/subtract/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..e02d93c5adf
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/infinity-throws-rangeerror.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.Duration.prototype.subtract throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.duration.prototype.subtract
+features: [Temporal]
+---*/
+
+const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const relativeTo = new Temporal.PlainDateTime(2000, 1, 1);
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: Infinity }, { relativeTo }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { relativeTo }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/length.js b/test/built-ins/Temporal/Duration/prototype/subtract/length.js
new file mode 100644
index 00000000000..e72d793524c
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: Temporal.Duration.prototype.subtract.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype.subtract, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/name.js b/test/built-ins/Temporal/Duration/prototype/subtract/name.js
new file mode 100644
index 00000000000..4e33d0f79ca
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: Temporal.Duration.prototype.subtract.name is "subtract".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype.subtract, "name", {
+ value: "subtract",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/negative-infinity-throws-rangeerror.js b/test/built-ins/Temporal/Duration/prototype/subtract/negative-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..f798ed52a29
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.Duration.prototype.subtract throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.duration.prototype.subtract
+features: [Temporal]
+---*/
+
+const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const relativeTo = new Temporal.PlainDateTime(2000, 1, 1);
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: -Infinity }, { relativeTo }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { relativeTo }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/non-integer-throws-rangeerror.js b/test/built-ins/Temporal/Duration/prototype/subtract/non-integer-throws-rangeerror.js
new file mode 100644
index 00000000000..17092e1ee87
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/non-integer-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 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 }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/not-a-constructor.js b/test/built-ins/Temporal/Duration/prototype/subtract/not-a-constructor.js
new file mode 100644
index 00000000000..03e6e0dfc75
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: >
+ Temporal.Duration.prototype.subtract does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Duration.prototype.subtract();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.prototype.subtract), false,
+ "isConstructor(Temporal.Duration.prototype.subtract)");
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/options-undefined.js b/test/built-ins/Temporal/Duration/prototype/subtract/options-undefined.js
new file mode 100644
index 00000000000..959fb77c613
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/options-undefined.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const duration1 = new Temporal.Duration(1);
+const duration2 = new Temporal.Duration(0, 12);
+assert.throws(RangeError, () => duration1.subtract(duration2), "default relativeTo is undefined");
+assert.throws(RangeError, () => duration1.subtract(duration2, undefined), "default relativeTo is undefined");
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/order-of-operations.js b/test/built-ins/Temporal/Duration/prototype/subtract/order-of-operations.js
new file mode 100644
index 00000000000..6f70b5b0609
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/order-of-operations.js
@@ -0,0 +1,74 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: Properties on an object passed to subtract() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 2, 1, 4, 5, 6, 7, 987, 654, 321);
+const relativeTo = new Temporal.PlainDateTime(2000, 1, 1);
+const expected = [
+ "get days",
+ "get days.valueOf",
+ "call days.valueOf",
+ "get hours",
+ "get hours.valueOf",
+ "call hours.valueOf",
+ "get microseconds",
+ "get microseconds.valueOf",
+ "call microseconds.valueOf",
+ "get milliseconds",
+ "get milliseconds.valueOf",
+ "call milliseconds.valueOf",
+ "get minutes",
+ "get minutes.valueOf",
+ "call minutes.valueOf",
+ "get months",
+ "get months.valueOf",
+ "call months.valueOf",
+ "get nanoseconds",
+ "get nanoseconds.valueOf",
+ "call nanoseconds.valueOf",
+ "get seconds",
+ "get seconds.valueOf",
+ "call seconds.valueOf",
+ "get weeks",
+ "get weeks.valueOf",
+ "call weeks.valueOf",
+ "get years",
+ "get years.valueOf",
+ "call years.valueOf",
+];
+const actual = [];
+const fields = {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = instance.subtract(argument, { relativeTo });
+TemporalHelpers.assertDuration(result, 0, 1, 0, 3, 4, 5, 6, 986, 653, 320);
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/subtract/prop-desc.js
new file mode 100644
index 00000000000..d2d2f3924b6
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: The "subtract" property of Temporal.Duration.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.prototype.subtract,
+ "function",
+ "`typeof Duration.prototype.subtract` is `function`"
+);
+
+verifyProperty(Temporal.Duration.prototype, "subtract", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/read-time-fields-before-datefromfields.js b/test/built-ins/Temporal/Duration/prototype/subtract/read-time-fields-before-datefromfields.js
new file mode 100644
index 00000000000..05efdc7fdd6
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/read-time-fields-before-datefromfields.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.duration.prototype.subtract step 5:
+ 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.g:
+ g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInvalidGettersTime();
+const duration1 = new Temporal.Duration(1);
+const duration2 = new Temporal.Duration(0, 12);
+duration1.subtract(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar } });
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-infinity-throws-rangeerror.js b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..4461f31f2a7
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.duration.prototype.subtract
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.subtract(instance, { relativeTo: { ...base, [prop]: inf } }), `${prop} property cannot be ${inf} in relativeTo`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.subtract(instance, { relativeTo: { ...base, [prop]: obj } }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..1dc97f3ac1d
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(0, 3);
+ assert.throws(RangeError, () => duration.subtract(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..7fded3d3667
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(0, 3);
+ assert.throws(RangeError, () => duration.subtract(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..46558f50d44
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(0, 3);
+ assert.throws(TypeError, () => duration.subtract(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-datetime.js b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-datetime.js
new file mode 100644
index 00000000000..8090505b6aa
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-datetime.js
@@ -0,0 +1,40 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: >
+ Conversion of ISO date-time strings as relativeTo option to
+ Temporal.ZonedDateTime or Temporal.PlainDateTime instances
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 1);
+
+let relativeTo = "2019-11-01T00:00";
+const result1 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+TemporalHelpers.assertDuration(result1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "bare date-time string is a plain relativeTo");
+
+relativeTo = "2019-11-01T00:00Z";
+const result2 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+TemporalHelpers.assertDuration(result2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z is a plain relativeTo");
+
+relativeTo = "2019-11-01T00:00-07:00";
+const result3 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+TemporalHelpers.assertDuration(result3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + offset is a plain relativeTo");
+
+relativeTo = "2019-11-01T00:00[America/Vancouver]";
+const result4 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+TemporalHelpers.assertDuration(result4, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, "date-time + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00Z[America/Vancouver]";
+const result5 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+TemporalHelpers.assertDuration(result5, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00-07:00[America/Vancouver]";
+const result6 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
+TemporalHelpers.assertDuration(result6, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, "date-time + offset + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00+04:15[America/Vancouver]";
+assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), "date-time + offset + IANA annotation throws if wall time and exact time mismatch");
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-invalid.js b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-invalid.js
new file mode 100644
index 00000000000..5bf459366c7
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-invalid.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: RangeError thrown if relativeTo is a string with the wrong format
+features: [Temporal]
+---*/
+
+['bad string', '15:30:45.123456', 'iso8601', 'UTC', 'P1YT1H'].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(1, 0, 0, 41);
+ assert.throws(RangeError, () => duration.subtract(new Temporal.Duration(0, 0, 0, 10), { relativeTo }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-plaindatetime.js b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-plaindatetime.js
new file mode 100644
index 00000000000..4fe06e34ec5
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-plaindatetime.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: The relativeTo option accepts a PlainDateTime-like ISO 8601 string
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+['2000-01-01', '2000-01-01T00:00', '2000-01-01T00:00[u-ca=iso8601]'].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(1, 0, 0, 41);
+ const result = duration.subtract(new Temporal.Duration(0, 0, 0, 10), { relativeTo });
+ TemporalHelpers.assertDuration(result, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0);
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-zoneddatetime.js b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-zoneddatetime.js
new file mode 100644
index 00000000000..db33ebf528c
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-zoneddatetime.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: The relativeTo option accepts a ZonedDateTime-like ISO 8601 string
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ '2000-01-01[UTC]',
+ '2000-01-01T00:00[UTC]',
+ '2000-01-01T00:00+00:00[UTC]',
+ '2000-01-01T00:00+00:00[UTC][u-ca=iso8601]',
+].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(1, 0, 0, 41);
+ const result = duration.subtract(new Temporal.Duration(0, 0, 0, 10), { relativeTo });
+ TemporalHelpers.assertDuration(result, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0);
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-negative-epochnanoseconds.js b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 00000000000..d607013e3e7
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const relativeTo = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+const duration = new Temporal.Duration(0, 0, 0, 1);
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time; in this
+// case via relativeTo.
+
+const result = duration.subtract(duration, { relativeTo });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..b7eda21c405
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(0, 3);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => duration.subtract(other, { relativeTo }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..03b9dbffc6f
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(0, 3);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => duration.subtract(other, { relativeTo }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..1e8b18f5097
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const other = new Temporal.Duration(0, 3);
+ const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => duration.subtract(other, { relativeTo }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/subclassing-ignored.js b/test/built-ins/Temporal/Duration/prototype/subtract/subclassing-ignored.js
new file mode 100644
index 00000000000..622f7f8c6f1
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.Duration,
+ [0, 0, 0, 4, 5, 6, 7, 987, 654, 321],
+ "subtract",
+ [{ nanoseconds: 1 }],
+ (result) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 5, 6, 7, 987, 654, 320),
+);
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/timezone-getpossibleinstantsfor-iterable.js b/test/built-ins/Temporal/Duration/prototype/subtract/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 00000000000..1ed510486c5
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.duration.prototype.subtract steps 5–6:
+ 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ 6. Let _result_ be ? AddDuration(_duration_.[[Years]], [...], _duration_.[[Nanoseconds]], −_other_.[[Years]], [...], −_other_.[[Nanoseconds]], _relativeTo_).
+ sec-temporal-torelativetemporalobject step 6.d:
+ d. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNs_, _timeZone_, *"compatible"*, *"reject"*).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-addduration steps 7.d–e and 7.g.i:
+ d. Let _intermediateNs_ be ? AddZonedDateTime(_relativeTo_.[[Nanoseconds]], _timeZone_, _calendar_, _y1_, [...], _ns1_).
+ e. Let _endNs_ be ? AddZonedDateTime(_intermediateNs_, _timeZone_, _calendar_, _y2_, [...], _ns2_).
+ [...]
+ i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
+ sec-temporal-differencezoneddatetime step 8:
+ 8. Let _intermediateNs_ be ? AddZonedDateTime(_ns1_, _timeZone_, _calendar_, _dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], 0, 0, 0, 0, 0, 0, 0).
+ sec-temporal-addzoneddatetime step 8:
+ 8. Let _intermediateInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _intermediateDateTime_, *"compatible"*).
+ sec-temporal-builtintimezonegetinstantfor step 1:
+ 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "2000-01-01T09:00:00", // called once on the input relativeTo object
+ "2001-01-01T09:00:00", // called once on relativeTo plus the receiver
+ "1999-12-01T09:00:00", // called once on relativeTo plus the receiver minus the argument
+ "1999-12-01T09:00:00", // called once on relativeTo plus the years, months, and weeks from the difference of relativeTo minus endNs
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const duration1 = new Temporal.Duration(1);
+ const duration2 = new Temporal.Duration(0, 13);
+ duration1.subtract(duration2, { relativeTo: { year: 2000, month: 1, day: 1, hour: 9, timeZone } });
+}, expected);
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/timezone-string-datetime.js b/test/built-ins/Temporal/Duration/prototype/subtract/timezone-string-datetime.js
new file mode 100644
index 00000000000..beeb55cf5a8
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/timezone-string-datetime.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.subtract
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "bare date-time string is not a time zone");
+assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), "bare date-time string is not a time zone");
+
+// The following are all valid strings so should not throw:
+
+[
+ "2021-08-19T17:30Z",
+ "2021-08-19T17:30-07:00",
+ "2021-08-19T17:30[America/Vancouver]",
+ "2021-08-19T17:30Z[America/Vancouver]",
+ "2021-08-19T17:30-07:00[America/Vancouver]",
+].forEach((timeZone) => {
+ instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
+ instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } });
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/toJSON/builtin.js b/test/built-ins/Temporal/Duration/prototype/toJSON/builtin.js
new file mode 100644
index 00000000000..ae71e23797d
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toJSON/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tojson
+description: >
+ Tests that Temporal.Duration.prototype.toJSON
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Duration.prototype.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Duration/prototype/toJSON/length.js b/test/built-ins/Temporal/Duration/prototype/toJSON/length.js
new file mode 100644
index 00000000000..e844f034e85
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toJSON/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tojson
+description: Temporal.Duration.prototype.toJSON.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/toJSON/name.js b/test/built-ins/Temporal/Duration/prototype/toJSON/name.js
new file mode 100644
index 00000000000..1d4e2135fa6
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toJSON/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tojson
+description: Temporal.Duration.prototype.toJSON.name is "toJSON".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/toJSON/not-a-constructor.js b/test/built-ins/Temporal/Duration/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 00000000000..0211d962ab9
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toJSON/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tojson
+description: >
+ Temporal.Duration.prototype.toJSON does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Duration.prototype.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.prototype.toJSON), false,
+ "isConstructor(Temporal.Duration.prototype.toJSON)");
diff --git a/test/built-ins/Temporal/Duration/prototype/toJSON/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/toJSON/prop-desc.js
new file mode 100644
index 00000000000..83ffd8eb65d
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toJSON/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tojson
+description: The "toJSON" property of Temporal.Duration.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.prototype.toJSON,
+ "function",
+ "`typeof Duration.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.Duration.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/toLocaleString/builtin.js b/test/built-ins/Temporal/Duration/prototype/toLocaleString/builtin.js
new file mode 100644
index 00000000000..f46d5d66d6f
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toLocaleString/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tolocalestring
+description: >
+ Tests that Temporal.Duration.prototype.toLocaleString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Duration.prototype.toLocaleString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.toLocaleString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.toLocaleString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.prototype.toLocaleString.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Duration/prototype/toLocaleString/length.js b/test/built-ins/Temporal/Duration/prototype/toLocaleString/length.js
new file mode 100644
index 00000000000..ea1a8916ae3
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toLocaleString/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tolocalestring
+description: Temporal.Duration.prototype.toLocaleString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype.toLocaleString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/toLocaleString/name.js b/test/built-ins/Temporal/Duration/prototype/toLocaleString/name.js
new file mode 100644
index 00000000000..aa34057dcd7
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toLocaleString/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tolocalestring
+description: Temporal.Duration.prototype.toLocaleString.name is "toLocaleString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype.toLocaleString, "name", {
+ value: "toLocaleString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/toLocaleString/not-a-constructor.js b/test/built-ins/Temporal/Duration/prototype/toLocaleString/not-a-constructor.js
new file mode 100644
index 00000000000..f7ec1104000
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toLocaleString/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tolocalestring
+description: >
+ Temporal.Duration.prototype.toLocaleString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Duration.prototype.toLocaleString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.prototype.toLocaleString), false,
+ "isConstructor(Temporal.Duration.prototype.toLocaleString)");
diff --git a/test/built-ins/Temporal/Duration/prototype/toLocaleString/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/toLocaleString/prop-desc.js
new file mode 100644
index 00000000000..b253706e0e0
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toLocaleString/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tolocalestring
+description: The "toLocaleString" property of Temporal.Duration.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.prototype.toLocaleString,
+ "function",
+ "`typeof Duration.prototype.toLocaleString` is `function`"
+);
+
+verifyProperty(Temporal.Duration.prototype, "toLocaleString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/toString/blank-duration-precision.js b/test/built-ins/Temporal/Duration/prototype/toString/blank-duration-precision.js
new file mode 100644
index 00000000000..ffd6179d11f
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toString/blank-duration-precision.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: >
+ Precision is handled correctly for blank durations, whether specified by
+ fractionalSecondDigits or smallestUnit
+features: [Temporal]
+---*/
+
+const blank = new Temporal.Duration();
+
+assert.sameValue(blank.toString({ fractionalSecondDigits: "auto" }), "PT0S");
+assert.sameValue(blank.toString({ fractionalSecondDigits: 0 }), "PT0S");
+assert.sameValue(blank.toString({ fractionalSecondDigits: 2 }), "PT0.00S");
+assert.sameValue(blank.toString({ fractionalSecondDigits: 9 }), "PT0.000000000S");
+
+assert.sameValue(blank.toString({ smallestUnit: "seconds" }), "PT0S");
+assert.sameValue(blank.toString({ smallestUnit: "milliseconds" }), "PT0.000S");
+assert.sameValue(blank.toString({ smallestUnit: "microseconds" }), "PT0.000000S");
+assert.sameValue(blank.toString({ smallestUnit: "nanoseconds" }), "PT0.000000000S");
diff --git a/test/built-ins/Temporal/Duration/prototype/toString/builtin.js b/test/built-ins/Temporal/Duration/prototype/toString/builtin.js
new file mode 100644
index 00000000000..37c7252fd3d
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toString/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: >
+ Tests that Temporal.Duration.prototype.toString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Duration.prototype.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-invalid-string.js b/test/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-invalid-string.js
new file mode 100644
index 00000000000..1cb51f820eb
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-invalid-string.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option not one of the allowed string values
+info: |
+ sec-getstringornumberoption step 4:
+ 4. If _stringValues_ is not *undefined* and _stringValues_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.duration.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0);
+
+assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: "other string" }));
diff --git a/test/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-nan.js b/test/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-nan.js
new file mode 100644
index 00000000000..42290865539
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-nan.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.duration.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0);
+assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: NaN }));
diff --git a/test/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-non-integer.js b/test/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-non-integer.js
new file mode 100644
index 00000000000..86d15aa2e82
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-non-integer.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: Rounding for fractionalSecondDigits option
+info: |
+ sec-getstringornumberoption step 3.b:
+ b. Return floor(ℝ(_value_)).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.duration.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0);
+
+const string = duration.toString({ fractionalSecondDigits: 2.5 });
+assert.sameValue(string, "P1Y2M3W4DT5H6M7.98S", "fractionalSecondDigits 2.5 floors to 2");
diff --git a/test/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-out-of-range.js b/test/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-out-of-range.js
new file mode 100644
index 00000000000..d9f4837d3cb
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-out-of-range.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option out of range
+info: |
+ sec-getstringornumberoption step 3.a:
+ a. If _value_ < _minimum_ or _value_ > _maximum_, throw a *RangeError* exception.
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.duration.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0);
+
+assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: -1 }));
+assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: 10 }));
diff --git a/test/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-undefined.js b/test/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-undefined.js
new file mode 100644
index 00000000000..2c3af23ae92
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: Fallback value for fractionalSecondDigits option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.duration.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0);
+
+const explicit = duration.toString({ fractionalSecondDigits: undefined });
+assert.sameValue(explicit, "P1Y2M3W4DT5H6M7.98765S", "default fractionalSecondDigits is auto");
+
+const implicit = duration.toString({});
+assert.sameValue(implicit, "P1Y2M3W4DT5H6M7.98765S", "default fractionalSecondDigits is auto");
diff --git a/test/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-wrong-type.js b/test/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-wrong-type.js
new file mode 100644
index 00000000000..143de7c3440
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: Type conversions for fractionalSecondDigits option
+info: |
+ sec-getoption steps 8–9:
+ 8. Else if _type_ is Number, then
+ a. Set _value_ to ? ToNumber(value).
+ b. ...
+ 9. Else,
+ a. Set _value_ to ? ToString(value).
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.duration.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0);
+TemporalHelpers.checkFractionalSecondDigitsOptionWrongType(duration);
diff --git a/test/built-ins/Temporal/Duration/prototype/toString/length.js b/test/built-ins/Temporal/Duration/prototype/toString/length.js
new file mode 100644
index 00000000000..1455cf20b67
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toString/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: Temporal.Duration.prototype.toString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/toString/name.js b/test/built-ins/Temporal/Duration/prototype/toString/name.js
new file mode 100644
index 00000000000..65d4c2d28a5
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toString/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: Temporal.Duration.prototype.toString.name is "toString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/toString/not-a-constructor.js b/test/built-ins/Temporal/Duration/prototype/toString/not-a-constructor.js
new file mode 100644
index 00000000000..60e7e378672
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toString/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: >
+ Temporal.Duration.prototype.toString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Duration.prototype.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.prototype.toString), false,
+ "isConstructor(Temporal.Duration.prototype.toString)");
diff --git a/test/built-ins/Temporal/Duration/prototype/toString/options-undefined.js b/test/built-ins/Temporal/Duration/prototype/toString/options-undefined.js
new file mode 100644
index 00000000000..c0d7bbfc7f1
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toString/options-undefined.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0);
+
+const explicit = duration.toString(undefined);
+assert.sameValue(explicit, "P1Y2M3W4DT5H6M7.98765S", "default precision is auto, and rounding is trunc");
+
+const implicit = duration.toString();
+assert.sameValue(implicit, "P1Y2M3W4DT5H6M7.98765S", "default precision is auto, and rounding is trunc");
diff --git a/test/built-ins/Temporal/Duration/prototype/toString/precision.js b/test/built-ins/Temporal/Duration/prototype/toString/precision.js
new file mode 100644
index 00000000000..05d79915b46
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toString/precision.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: toString() produces a fractional part of the correct length
+features: [Temporal]
+---*/
+
+const { Duration } = Temporal;
+
+const durationString = 'PT0.084000159S';
+const duration = Duration.from(durationString);
+const precisionString = duration.toString({
+ smallestUnit: 'milliseconds'
+});
+
+assert.sameValue(durationString, duration.toString());
+assert.sameValue(precisionString, "PT0.084S");
diff --git a/test/built-ins/Temporal/Duration/prototype/toString/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/toString/prop-desc.js
new file mode 100644
index 00000000000..7d5d7391a9e
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toString/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: The "toString" property of Temporal.Duration.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.prototype.toString,
+ "function",
+ "`typeof Duration.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.Duration.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/toString/roundingmode-invalid-string.js b/test/built-ins/Temporal/Duration/prototype/toString/roundingmode-invalid-string.js
new file mode 100644
index 00000000000..983b9a25b67
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toString/roundingmode-invalid-string.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+assert.throws(RangeError, () => duration.toString({ smallestUnit: "microsecond", roundingMode: "other string" }));
diff --git a/test/built-ins/Temporal/Duration/prototype/toString/roundingmode-undefined.js b/test/built-ins/Temporal/Duration/prototype/toString/roundingmode-undefined.js
new file mode 100644
index 00000000000..ea7bcdfe7ba
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toString/roundingmode-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: Fallback value for roundingMode option
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+
+const explicit1 = duration.toString({ smallestUnit: "microsecond", roundingMode: undefined });
+assert.sameValue(explicit1, "PT12H34M56.123987S", "default roundingMode is trunc");
+const implicit1 = duration.toString({ smallestUnit: "microsecond" });
+assert.sameValue(implicit1, "PT12H34M56.123987S", "default roundingMode is trunc");
+
+const explicit2 = duration.toString({ smallestUnit: "millisecond", roundingMode: undefined });
+assert.sameValue(explicit2, "PT12H34M56.123S", "default roundingMode is trunc");
+const implicit2 = duration.toString({ smallestUnit: "millisecond" });
+assert.sameValue(implicit2, "PT12H34M56.123S", "default roundingMode is trunc");
+
+const explicit3 = duration.toString({ smallestUnit: "second", roundingMode: undefined });
+assert.sameValue(explicit3, "PT12H34M56S", "default roundingMode is trunc");
+const implicit3 = duration.toString({ smallestUnit: "second" });
+assert.sameValue(implicit3, "PT12H34M56S", "default roundingMode is trunc");
diff --git a/test/built-ins/Temporal/Duration/prototype/toString/roundingmode-wrong-type.js b/test/built-ins/Temporal/Duration/prototype/toString/roundingmode-wrong-type.js
new file mode 100644
index 00000000000..64ad7c4966a
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toString/roundingmode-wrong-type.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => duration.toString({ smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => assert.sameValue(result, "PT12H34M56.123987S", descr),
+);
diff --git a/test/built-ins/Temporal/Duration/prototype/toString/smallestunit-invalid-string.js b/test/built-ins/Temporal/Duration/prototype/toString/smallestunit-invalid-string.js
new file mode 100644
index 00000000000..b41620c1f4d
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toString/smallestunit-invalid-string.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+assert.throws(RangeError, () => duration.toString({ smallestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/Duration/prototype/toString/smallestunit-plurals-accepted.js b/test/built-ins/Temporal/Duration/prototype/toString/smallestunit-plurals-accepted.js
new file mode 100644
index 00000000000..5c2fc343d38
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toString/smallestunit-plurals-accepted.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const validUnits = [
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => duration.toString({ smallestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/Duration/prototype/toString/smallestunit-undefined.js b/test/built-ins/Temporal/Duration/prototype/toString/smallestunit-undefined.js
new file mode 100644
index 00000000000..e5db0278636
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toString/smallestunit-undefined.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: Fallback value for smallestUnit option
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+
+const explicit1 = duration.toString({ smallestUnit: undefined, fractionalSecondDigits: 6 });
+assert.sameValue(explicit1, "PT12H34M56.123987S", "default smallestUnit defers to fractionalSecondDigits");
+const implicit1 = duration.toString({ fractionalSecondDigits: 6 });
+assert.sameValue(implicit1, "PT12H34M56.123987S", "default smallestUnit defers to fractionalSecondDigits");
+
+const explicit2 = duration.toString({ smallestUnit: undefined, fractionalSecondDigits: 3 });
+assert.sameValue(explicit2, "PT12H34M56.123S", "default smallestUnit defers to fractionalSecondDigits");
+const implicit2 = duration.toString({ fractionalSecondDigits: 3 });
+assert.sameValue(implicit2, "PT12H34M56.123S", "default smallestUnit defers to fractionalSecondDigits");
diff --git a/test/built-ins/Temporal/Duration/prototype/toString/smallestunit-valid-units.js b/test/built-ins/Temporal/Duration/prototype/toString/smallestunit-valid-units.js
new file mode 100644
index 00000000000..fce836b31b1
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toString/smallestunit-valid-units.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: Valid units for the smallestUnit option
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+
+assert.sameValue(duration.toString({ smallestUnit: "second" }), "P1Y2M3W4DT5H6M7S");
+assert.sameValue(duration.toString({ smallestUnit: "millisecond" }), "P1Y2M3W4DT5H6M7.987S");
+assert.sameValue(duration.toString({ smallestUnit: "microsecond" }), "P1Y2M3W4DT5H6M7.987654S");
+assert.sameValue(duration.toString({ smallestUnit: "nanosecond" }), "P1Y2M3W4DT5H6M7.987654321S");
+
+const notValid = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+];
+
+notValid.forEach((smallestUnit) => {
+ assert.throws(RangeError, () => duration.toString({ smallestUnit }), smallestUnit);
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/toString/smallestunit-wrong-type.js b/test/built-ins/Temporal/Duration/prototype/toString/smallestunit-wrong-type.js
new file mode 100644
index 00000000000..1740901401c
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/toString/smallestunit-wrong-type.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.tostring
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => duration.toString({ smallestUnit }),
+ (result, descr) => assert.sameValue(result, "PT12H34M56.123987S", descr),
+);
diff --git a/test/built-ins/Temporal/Duration/prototype/total/balance-negative-result.js b/test/built-ins/Temporal/Duration/prototype/total/balance-negative-result.js
new file mode 100644
index 00000000000..d1af0064258
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/balance-negative-result.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: A negative duration result is balanced correctly by the modulo operation in NanosecondsToDays
+info: |
+ sec-temporal-nanosecondstodays step 6:
+ 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
+ sec-temporal-balanceduration step 4:
+ 4. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_).
+ sec-temporal.duration.prototype.round step 9:
+ 9. Let _balanceResult_ be ? BalanceDuration(_unbalanceResult_.[[Days]], _unbalanceResult_.[[Hours]], _unbalanceResult_.[[Minutes]], _unbalanceResult_.[[Seconds]], _unbalanceResult_.[[Milliseconds]], _unbalanceResult_.[[Microseconds]], _unbalanceResult_.[[Nanoseconds]], _unit_, _intermediate_).
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 0, -60);
+const result = duration.total({ unit: "days" });
+assert.sameValue(result, -2.5);
diff --git a/test/built-ins/Temporal/Duration/prototype/total/builtin.js b/test/built-ins/Temporal/Duration/prototype/total/builtin.js
new file mode 100644
index 00000000000..672799e80a2
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: >
+ Tests that Temporal.Duration.prototype.total
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Duration.prototype.total),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.total),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.total),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.prototype.total.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Duration/prototype/total/calendar-dateuntil-called-with-singular-largestunit.js b/test/built-ins/Temporal/Duration/prototype/total/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 00000000000..d9fb7ec05e7
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,93 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.duration.prototype.total steps 7–11:
+ 7. Let _unbalanceResult_ be ? UnbalanceDurationRelative(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _unit_, _relativeTo_).
+ ...
+ 10. Let _balanceResult_ be ? BalanceDuration(_unbalanceResult_.[[Days]], _unbalanceResult_.[[Hours]], _unbalanceResult_.[[Minutes]], _unbalanceResult_.[[Seconds]], _unbalanceResult_.[[Milliseconds]], _unbalanceResult_.[[Microseconds]], _unbalanceResult_.[[Nanoseconds]], _unit_, _intermediate_).
+ 11. Let _roundResult_ be ? RoundDuration(_unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], _balanceResult_.[[Days]], _balanceResult_.[[Hours]], _balanceResult_.[[Minutes]], _balanceResult_.[[Seconds]], _balanceResult_.[[Milliseconds]], _balanceResult_.[[Microseconds]], _balanceResult_.[[Nanoseconds]], 1, _unit_, *"trunc"*, _relativeTo_).
+ sec-temporal-unbalancedurationrelative steps 1 and 9.d.iii–v:
+ 1. If _largestUnit_ is *"year"*, or _years_, _months_, _weeks_, and _days_ are all 0, then
+ a. Return ...
+ ...
+ 9. If _largestUnit_ is *"month"*, then
+ ...
+ d. Repeat, while abs(_years_) > 0,
+ ...
+ iii. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
+ iv. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*).
+ v. Let _untilResult_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _newRelativeTo_, _untilOptions_, _dateUntil_).
+ sec-temporal-balanceduration step 3.a:
+ 3. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_).
+ sec-temporal-roundduration steps 5.d and 8.n–p:
+ 5. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
+ ...
+ 8. If _unit_ is *"year"*, then
+ ...
+ n. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
+ o. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"year"*).
+ p. Let _timePassed_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _daysLater_, _untilOptions_)
+ sec-temporal-nanosecondstodays step 11:
+ 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*).
+ sec-temporal-differenceisodatetime steps 9–11:
+ 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
+ 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
+ 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Check the paths that go through NanosecondsToDays: one call to dateUntil() in
+// BalanceDuration and one in RoundDuration with largestUnit: "day" when the
+// unit is "year", "month", "week", or "day", and one extra call with
+// largestUnit: "year" in RoundDuration when the unit is "year".
+
+const duration = new Temporal.Duration(0, 1, 1, 1, 1, 1, 1, 1, 1, 1);
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, unit) => {
+ const relativeTo = new Temporal.ZonedDateTime(0n, "UTC", calendar);
+ duration.total({ unit, relativeTo });
+ },
+ {
+ years: ["day", "day", "year"],
+ months: ["day", "day"],
+ weeks: ["day", "day"],
+ days: ["day", "day"],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+// Check the path that converts years to months in UnbalanceDurationRelative.
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, unit) => {
+ const duration = new Temporal.Duration(5);
+ const relativeTo = new Temporal.PlainDateTime(2000, 5, 2, 0, 0, 0, 0, 0, 0, calendar);
+ duration.total({ unit, relativeTo });
+ },
+ {
+ years: ["year"],
+ months: ["month", "month", "month", "month", "month"],
+ weeks: [],
+ days: [],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
diff --git a/test/built-ins/Temporal/Duration/prototype/total/calendar-fields-iterable.js b/test/built-ins/Temporal/Duration/prototype/total/calendar-fields-iterable.js
new file mode 100644
index 00000000000..0bd730fca5a
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/calendar-fields-iterable.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.duration.prototype.total step 4:
+ 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1);
+duration.total({ unit: 'seconds', relativeTo: { year: 2000, month: 1, day: 1, calendar } });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/Duration/prototype/total/calendar-temporal-object.js b/test/built-ins/Temporal/Duration/prototype/total/calendar-temporal-object.js
new file mode 100644
index 00000000000..22b3939e177
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.duration.prototype.total step 4:
+ 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1);
+ duration.total({ unit: 'seconds', relativeTo: { year: 2000, month: 1, day: 1, calendar: temporalObject } });
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/total/dateuntil-field.js b/test/built-ins/Temporal/Duration/prototype/total/dateuntil-field.js
new file mode 100644
index 00000000000..847c9e775c5
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/dateuntil-field.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: >
+ When consulting calendar.dateUntil() to calculate the number of months in a
+ year, the months property is not accessed on the result Duration
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "call dateUntil",
+ "call dateUntil",
+];
+
+const duration = new Temporal.Duration(0, 12);
+TemporalHelpers.observeProperty(actual, duration, "months", 1);
+
+const calendar = TemporalHelpers.calendarDateUntilObservable(actual, duration);
+const relativeTo = new Temporal.PlainDateTime(2018, 10, 12, 0, 0, 0, 0, 0, 0, calendar);
+
+const years = new Temporal.Duration(2);
+const result = years.total({ unit: "months", relativeTo });
+assert.sameValue(result, 24, "result");
+assert.compareArray(actual, expected, "operations");
diff --git a/test/built-ins/Temporal/Duration/prototype/total/length.js b/test/built-ins/Temporal/Duration/prototype/total/length.js
new file mode 100644
index 00000000000..c3ba5a6ad4d
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: Temporal.Duration.prototype.total.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype.total, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/total/name.js b/test/built-ins/Temporal/Duration/prototype/total/name.js
new file mode 100644
index 00000000000..988dc62191e
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: Temporal.Duration.prototype.total.name is "total".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype.total, "name", {
+ value: "total",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/total/not-a-constructor.js b/test/built-ins/Temporal/Duration/prototype/total/not-a-constructor.js
new file mode 100644
index 00000000000..ea7d0d0da5d
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: >
+ Temporal.Duration.prototype.total does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Duration.prototype.total();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.prototype.total), false,
+ "isConstructor(Temporal.Duration.prototype.total)");
diff --git a/test/built-ins/Temporal/Duration/prototype/total/options-wrong-type.js b/test/built-ins/Temporal/Duration/prototype/total/options-wrong-type.js
new file mode 100644
index 00000000000..671a9d73195
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/options-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: TypeError thrown when options argument is missing or a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const values = [
+ undefined,
+ null,
+ true,
+ "string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.Duration(0, 0, 0, 0, 1);
+assert.throws(TypeError, () => instance.total(), "TypeError on missing argument");
+values.forEach((value) => {
+ assert.throws(TypeError, () => instance.total(value), `TypeError on wrong argument type ${typeof(value)}`);
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/total/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/total/prop-desc.js
new file mode 100644
index 00000000000..f371deda538
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: The "total" property of Temporal.Duration.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.prototype.total,
+ "function",
+ "`typeof Duration.prototype.total` is `function`"
+);
+
+verifyProperty(Temporal.Duration.prototype, "total", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/total/read-time-fields-before-datefromfields.js b/test/built-ins/Temporal/Duration/prototype/total/read-time-fields-before-datefromfields.js
new file mode 100644
index 00000000000..ae4f10bcbf4
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/read-time-fields-before-datefromfields.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.duration.prototype.total step 4:
+ 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ sec-temporal-torelativetemporalobject step 4.g:
+ g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInvalidGettersTime();
+const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1);
+duration.total({ unit: 'seconds', relativeTo: { year: 2000, month: 1, day: 1, calendar } });
diff --git a/test/built-ins/Temporal/Duration/prototype/total/relativeto-infinity-throws-rangeerror.js b/test/built-ins/Temporal/Duration/prototype/total/relativeto-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..d2b4f125ffa
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/relativeto-infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.duration.prototype.total
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.total({ unit: "seconds", relativeTo: { ...base, [prop]: inf } }), `${prop} property cannot be ${inf} in relativeTo`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.total({ unit: "seconds", relativeTo: { ...base, [prop]: obj } }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..216d9aadc8e
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ assert.throws(RangeError, () => duration.total({ unit: "seconds", relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..a7975327064
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ assert.throws(RangeError, () => duration.total({ unit: "seconds", relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..dcd73d3525a
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ assert.throws(TypeError, () => duration.total({ unit: "seconds", relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/total/relativeto-string-datetime.js b/test/built-ins/Temporal/Duration/prototype/total/relativeto-string-datetime.js
new file mode 100644
index 00000000000..4572db8d097
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/relativeto-string-datetime.js
@@ -0,0 +1,39 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: >
+ Conversion of ISO date-time strings as relativeTo option to
+ Temporal.ZonedDateTime or Temporal.PlainDateTime instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 0, 0, 0, 24);
+
+let relativeTo = "2019-11-01T00:00";
+const result1 = instance.total({ unit: "days", relativeTo });
+assert.sameValue(result1, 367, "bare date-time string is a plain relativeTo");
+
+relativeTo = "2019-11-01T00:00Z";
+const result2 = instance.total({ unit: "days", relativeTo });
+assert.sameValue(result2, 367, "date-time + Z is a plain relativeTo");
+
+relativeTo = "2019-11-01T00:00-07:00";
+const result3 = instance.total({ unit: "days", relativeTo });
+assert.sameValue(result3, 367, "date-time + offset is a plain relativeTo");
+
+relativeTo = "2019-11-01T00:00[America/Vancouver]";
+const result4 = instance.total({ unit: "days", relativeTo });
+assert.sameValue(result4, 366.96, "date-time + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00Z[America/Vancouver]";
+const result5 = instance.total({ unit: "days", relativeTo });
+assert.sameValue(result5, 366.96, "date-time + Z + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00-07:00[America/Vancouver]";
+const result6 = instance.total({ unit: "days", relativeTo });
+assert.sameValue(result6, 366.96, "date-time + offset + IANA annotation is a zoned relativeTo");
+
+relativeTo = "2019-11-01T00:00+04:15[America/Vancouver]";
+assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }), "date-time + offset + IANA annotation throws if wall time and exact time mismatch");
diff --git a/test/built-ins/Temporal/Duration/prototype/total/relativeto-string-invalid.js b/test/built-ins/Temporal/Duration/prototype/total/relativeto-string-invalid.js
new file mode 100644
index 00000000000..fa321241bb0
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/relativeto-string-invalid.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: RangeError thrown if relativeTo is a string with the wrong format
+features: [Temporal]
+---*/
+
+['bad string', '15:30:45.123456', 'iso8601', 'UTC', 'P1YT1H'].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(0, 0, 0, 31);
+ assert.throws(RangeError, () => duration.total({ unit: "months", relativeTo }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime.js b/test/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime.js
new file mode 100644
index 00000000000..b37250d7109
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: The relativeTo option accepts a PlainDateTime-like ISO 8601 string
+features: [Temporal]
+---*/
+
+['2000-01-01', '2000-01-01T00:00', '2000-01-01T00:00[u-ca=iso8601]'].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(0, 0, 0, 31);
+ const result = duration.total({ unit: "months", relativeTo });
+ assert.sameValue(result, 1);
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/total/relativeto-string-zoneddatetime.js b/test/built-ins/Temporal/Duration/prototype/total/relativeto-string-zoneddatetime.js
new file mode 100644
index 00000000000..ebbd3d3f471
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/relativeto-string-zoneddatetime.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: The relativeTo option accepts a ZonedDateTime-like ISO 8601 string
+features: [Temporal]
+---*/
+
+[
+ '2000-01-01[UTC]',
+ '2000-01-01T00:00[UTC]',
+ '2000-01-01T00:00+00:00[UTC]',
+ '2000-01-01T00:00+00:00[UTC][u-ca=iso8601]',
+].forEach((relativeTo) => {
+ const duration = new Temporal.Duration(0, 0, 0, 31);
+ const result = duration.total({ unit: "months", relativeTo });
+ assert.sameValue(result, 1);
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/total/relativeto-undefined-throw-on-calendar-units.js b/test/built-ins/Temporal/Duration/prototype/total/relativeto-undefined-throw-on-calendar-units.js
new file mode 100644
index 00000000000..7014f4fcdef
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/relativeto-undefined-throw-on-calendar-units.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: >
+ The relativeTo option is required when the Duration contains years, months,
+ or weeks, and largestUnit is days; or largestUnit is weeks or months
+features: [Temporal]
+---*/
+
+const oneYear = new Temporal.Duration(1);
+const oneMonth = new Temporal.Duration(0, 1);
+const oneWeek = new Temporal.Duration(0, 0, 1);
+const oneDay = new Temporal.Duration(0, 0, 0, 1);
+
+const options = { unit: "days" };
+assert.sameValue(oneDay.total(options), 1, "days do not require relativeTo");
+assert.throws(RangeError, () => oneWeek.total(options), "total days of weeks requires relativeTo");
+assert.throws(RangeError, () => oneMonth.total(options), "total days of months requires relativeTo");
+assert.throws(RangeError, () => oneYear.total(options), "total days of years requires relativeTo");
+
+["months", "weeks"].forEach((unit) => {
+ [oneDay, oneWeek, oneMonth, oneYear].forEach((duration) => {
+ assert.throws(RangeError, () => duration.total({ unit }), `${duration} total ${unit} requires relativeTo`);
+ });
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-negative-epochnanoseconds.js b/test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 00000000000..8b44f7747bf
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const relativeTo = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+const duration = new Temporal.Duration(0, 0, 0, 1);
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time; in this
+// case via relativeTo.
+
+const result = duration.total({ relativeTo, unit: "days" });
+assert.sameValue(result, 1);
diff --git a/test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..826dde446d6
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => duration.total({ unit: "seconds", relativeTo: datetime }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..88835003fba
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => duration.total({ unit: "seconds", relativeTo: datetime }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..2172e065077
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => duration.total({ unit: "seconds", relativeTo: datetime }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/total/timezone-getpossibleinstantsfor-iterable.js b/test/built-ins/Temporal/Duration/prototype/total/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 00000000000..bc13e0bd9d6
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.duration.prototype.total steps 4 and 10:
+ 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
+ 10. Let _balanceResult_ be ? BalanceDuration(_unbalanceResult_.[[Days]], [...], _unbalanceResult_.[[Nanoseconds]], _unit_, _intermediate_).
+ sec-temporal-torelativetemporalobject step 6.d:
+ d. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNs_, _timeZone_, *"compatible"*, *"reject"*).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-addzoneddatetime step 8:
+ 8. Let _intermediateInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _intermediateDateTime_, *"compatible"*).
+ sec-temporal-builtintimezonegetinstantfor step 1:
+ 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "2000-01-01T00:00:00", // called once on the input relativeTo if ZonedDateTime
+ "2001-02-09T00:00:00", // called once on the intermediate ZonedDateTime with the calendar parts of the Duration added
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1);
+ duration.total({ unit: 'seconds', relativeTo: { year: 2000, month: 1, day: 1, timeZone } });
+}, expected);
diff --git a/test/built-ins/Temporal/Duration/prototype/total/timezone-string-datetime.js b/test/built-ins/Temporal/Duration/prototype/total/timezone-string-datetime.js
new file mode 100644
index 00000000000..80984d99308
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/timezone-string-datetime.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "bare date-time string is not a time zone");
+assert.throws(RangeError, () => instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), "bare date-time string is not a time zone");
+
+// The following are all valid strings so should not throw:
+
+[
+ "2021-08-19T17:30Z",
+ "2021-08-19T17:30-07:00",
+ "2021-08-19T17:30[America/Vancouver]",
+ "2021-08-19T17:30Z[America/Vancouver]",
+ "2021-08-19T17:30-07:00[America/Vancouver]",
+].forEach((timeZone) => {
+ instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
+ instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } });
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/total/unit-invalid-string.js b/test/built-ins/Temporal/Duration/prototype/total/unit-invalid-string.js
new file mode 100644
index 00000000000..0b156a7ec1e
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/unit-invalid-string.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.protoype.total
+description: RangeError thrown when unit option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaldurationtotal step 1:
+ 1. Let _unit_ be ? GetOption(_normalizedOptions_, *"unit"*, « String », « *"year"*, *"years"*, *"month"*, *"months"*, *"week"*, *"weeks"*, *"day"*, *"days"*, *"hour"*, *"hours"*, *"minute"*, *"minutes"*, *"second"*, *"seconds"*, *"millisecond"*, *"milliseconds"*, *"microsecond"*, *"microseconds"*, *"nanosecond"*, *"nanoseconds"* », *undefined*).
+ sec-temporal.duration.protoype.total step 5:
+ 5. Let _unit_ be ? ToTemporalDurationTotalUnit(_options_).
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 1);
+assert.throws(RangeError, () => duration.total({ unit: "other string" }));
diff --git a/test/built-ins/Temporal/Duration/prototype/total/unit-plurals-accepted.js b/test/built-ins/Temporal/Duration/prototype/total/unit-plurals-accepted.js
new file mode 100644
index 00000000000..fec9bc2064b
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/unit-plurals-accepted.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: Plural units are accepted as well for the unit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const relativeTo = new Temporal.PlainDate(2000, 1, 1);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((unit) => duration.total({ unit, relativeTo }), validUnits);
diff --git a/test/built-ins/Temporal/Duration/prototype/total/unit-wrong-type.js b/test/built-ins/Temporal/Duration/prototype/total/unit-wrong-type.js
new file mode 100644
index 00000000000..15bb0823cf2
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/total/unit-wrong-type.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.total
+description: Type conversions for unit option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaldurationtotal step 1:
+ 1. Let _unit_ be ? GetOption(_normalizedOptions_, *"unit"*, « String », « *"year"*, *"years"*, *"month"*, *"months"*, *"week"*, *"weeks"*, *"day"*, *"days"*, *"hour"*, *"hours"*, *"minute"*, *"minutes"*, *"second"*, *"seconds"*, *"millisecond"*, *"milliseconds"*, *"microsecond"*, *"microseconds"*, *"nanosecond"*, *"nanoseconds"* », *undefined*).
+ sec-temporal.duration.protoype.total step 5:
+ 5. Let _unit_ be ? ToTemporalDurationTotalUnit(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const duration = new Temporal.Duration(0, 0, 0, 1);
+TemporalHelpers.checkStringOptionWrongType("unit", "hour",
+ (unit) => duration.total({ unit }),
+ (result, descr) => assert.sameValue(result, 24, descr),
+);
diff --git a/test/built-ins/Temporal/Duration/prototype/valueOf/builtin.js b/test/built-ins/Temporal/Duration/prototype/valueOf/builtin.js
new file mode 100644
index 00000000000..96907e526a7
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/valueOf/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.valueof
+description: >
+ Tests that Temporal.Duration.prototype.valueOf
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Duration.prototype.valueOf),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.valueOf),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.valueOf),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.prototype.valueOf.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Duration/prototype/valueOf/length.js b/test/built-ins/Temporal/Duration/prototype/valueOf/length.js
new file mode 100644
index 00000000000..2cbc90b3b73
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/valueOf/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.valueof
+description: Temporal.Duration.prototype.valueOf.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype.valueOf, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/valueOf/name.js b/test/built-ins/Temporal/Duration/prototype/valueOf/name.js
new file mode 100644
index 00000000000..8ca02c44516
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/valueOf/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.valueof
+description: Temporal.Duration.prototype.valueOf.name is "valueOf".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype.valueOf, "name", {
+ value: "valueOf",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/valueOf/not-a-constructor.js b/test/built-ins/Temporal/Duration/prototype/valueOf/not-a-constructor.js
new file mode 100644
index 00000000000..12242b8e62a
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/valueOf/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.valueof
+description: >
+ Temporal.Duration.prototype.valueOf does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Duration.prototype.valueOf();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.prototype.valueOf), false,
+ "isConstructor(Temporal.Duration.prototype.valueOf)");
diff --git a/test/built-ins/Temporal/Duration/prototype/valueOf/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/valueOf/prop-desc.js
new file mode 100644
index 00000000000..511a6b30a8e
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/valueOf/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.valueof
+description: The "valueOf" property of Temporal.Duration.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.prototype.valueOf,
+ "function",
+ "`typeof Duration.prototype.valueOf` is `function`"
+);
+
+verifyProperty(Temporal.Duration.prototype, "valueOf", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/weeks/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/weeks/prop-desc.js
new file mode 100644
index 00000000000..c8fc13986c2
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/weeks/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.duration.prototype.weeks
+description: The "weeks" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "weeks");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/Duration/prototype/with/builtin.js b/test/built-ins/Temporal/Duration/prototype/with/builtin.js
new file mode 100644
index 00000000000..023b5c1c95b
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/with/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.with
+description: >
+ Tests that Temporal.Duration.prototype.with
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Duration.prototype.with),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.with),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.with),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Duration.prototype.with.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Duration/prototype/with/infinity-throws-rangeerror.js b/test/built-ins/Temporal/Duration/prototype/with/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..3816d895af3
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/with/infinity-throws-rangeerror.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.Duration.prototype.with handles a property bag if any value is Infinity
+esid: sec-temporal.duration.prototype.with
+features: [Temporal]
+---*/
+
+const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.with({ [field]: Infinity }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.with({ [field]: obj }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/with/length.js b/test/built-ins/Temporal/Duration/prototype/with/length.js
new file mode 100644
index 00000000000..d53ba91b860
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/with/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.with
+description: Temporal.Duration.prototype.with.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype.with, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/with/name.js b/test/built-ins/Temporal/Duration/prototype/with/name.js
new file mode 100644
index 00000000000..0e83341a901
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/with/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.with
+description: Temporal.Duration.prototype.with.name is "with".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Duration.prototype.with, "name", {
+ value: "with",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/with/negative-infinity-throws-rangeerror.js b/test/built-ins/Temporal/Duration/prototype/with/negative-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..d1fd8e5d9a6
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/with/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.Duration.prototype.with throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.duration.prototype.with
+features: [Temporal]
+---*/
+
+const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.with({ [field]: -Infinity }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.with({ [field]: obj }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/with/non-integer-throws-rangeerror.js b/test/built-ins/Temporal/Duration/prototype/with/non-integer-throws-rangeerror.js
new file mode 100644
index 00000000000..db746ccaa0f
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/with/non-integer-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.with
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.with({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.with({ [field]: -1.5 }));
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/with/not-a-constructor.js b/test/built-ins/Temporal/Duration/prototype/with/not-a-constructor.js
new file mode 100644
index 00000000000..3849a56c164
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/with/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.with
+description: >
+ Temporal.Duration.prototype.with does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Duration.prototype.with();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Duration.prototype.with), false,
+ "isConstructor(Temporal.Duration.prototype.with)");
diff --git a/test/built-ins/Temporal/Duration/prototype/with/order-of-operations.js b/test/built-ins/Temporal/Duration/prototype/with/order-of-operations.js
new file mode 100644
index 00000000000..acb83dc952f
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/with/order-of-operations.js
@@ -0,0 +1,73 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.with
+description: Properties on an object passed to with() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const expected = [
+ "get days",
+ "get days.valueOf",
+ "call days.valueOf",
+ "get hours",
+ "get hours.valueOf",
+ "call hours.valueOf",
+ "get microseconds",
+ "get microseconds.valueOf",
+ "call microseconds.valueOf",
+ "get milliseconds",
+ "get milliseconds.valueOf",
+ "call milliseconds.valueOf",
+ "get minutes",
+ "get minutes.valueOf",
+ "call minutes.valueOf",
+ "get months",
+ "get months.valueOf",
+ "call months.valueOf",
+ "get nanoseconds",
+ "get nanoseconds.valueOf",
+ "call nanoseconds.valueOf",
+ "get seconds",
+ "get seconds.valueOf",
+ "call seconds.valueOf",
+ "get weeks",
+ "get weeks.valueOf",
+ "call weeks.valueOf",
+ "get years",
+ "get years.valueOf",
+ "call years.valueOf",
+];
+const actual = [];
+const fields = {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = instance.with(argument);
+TemporalHelpers.assertDuration(result, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/Duration/prototype/with/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/with/prop-desc.js
new file mode 100644
index 00000000000..811e7652ad5
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/with/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.with
+description: The "with" property of Temporal.Duration.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Duration.prototype.with,
+ "function",
+ "`typeof Duration.prototype.with` is `function`"
+);
+
+verifyProperty(Temporal.Duration.prototype, "with", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Duration/prototype/with/subclassing-ignored.js b/test/built-ins/Temporal/Duration/prototype/with/subclassing-ignored.js
new file mode 100644
index 00000000000..330ca3ea6cf
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/with/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration.prototype.with
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.Duration,
+ [0, 0, 0, 4, 5, 6, 7, 987, 654, 321],
+ "with",
+ [{ nanoseconds: 1 }],
+ (result) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 5, 6, 7, 987, 654, 1),
+);
diff --git a/test/built-ins/Temporal/Duration/prototype/years/prop-desc.js b/test/built-ins/Temporal/Duration/prototype/years/prop-desc.js
new file mode 100644
index 00000000000..9fb001cedb9
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/prototype/years/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.duration.prototype.years
+description: The "years" property of Temporal.Duration.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "years");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/Duration/seconds-undefined.js b/test/built-ins/Temporal/Duration/seconds-undefined.js
new file mode 100644
index 00000000000..a4cc4add4a0
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/seconds-undefined.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration
+description: Undefined arguments should be treated as zero.
+features: [Temporal]
+---*/
+
+const args = [1, 1, 1, 1, 1, 1];
+
+const explicit = new Temporal.Duration(...args, undefined);
+assert.sameValue(explicit.seconds, 0, "seconds default argument");
+
+const implicit = new Temporal.Duration(...args);
+assert.sameValue(implicit.seconds, 0, "seconds default argument");
diff --git a/test/built-ins/Temporal/Duration/weeks-undefined.js b/test/built-ins/Temporal/Duration/weeks-undefined.js
new file mode 100644
index 00000000000..d01172db2d3
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/weeks-undefined.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration
+description: Undefined arguments should be treated as zero.
+features: [Temporal]
+---*/
+
+const args = [1, 1];
+
+const explicit = new Temporal.Duration(...args, undefined);
+assert.sameValue(explicit.weeks, 0, "weeks default argument");
+
+const implicit = new Temporal.Duration(...args);
+assert.sameValue(implicit.weeks, 0, "weeks default argument");
diff --git a/test/built-ins/Temporal/Duration/years-undefined.js b/test/built-ins/Temporal/Duration/years-undefined.js
new file mode 100644
index 00000000000..55235840916
--- /dev/null
+++ b/test/built-ins/Temporal/Duration/years-undefined.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.duration
+description: Undefined arguments should be treated as zero.
+features: [Temporal]
+---*/
+
+const explicit = new Temporal.Duration(undefined);
+assert.sameValue(explicit.years, 0, "years default argument");
+
+const implicit = new Temporal.Duration();
+assert.sameValue(implicit.years, 0, "years default argument");
diff --git a/test/built-ins/Temporal/Instant/argument.js b/test/built-ins/Temporal/Instant/argument.js
new file mode 100644
index 00000000000..c967ee1dafe
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/argument.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant
+description: TypeError thrown if input is of wrong primitive type
+features: [Symbol, Temporal]
+---*/
+
+assert.throws(TypeError, () => new Temporal.Instant(), "undefined");
+assert.throws(TypeError, () => new Temporal.Instant(undefined), "undefined");
+assert.throws(TypeError, () => new Temporal.Instant(null), "null");
+assert.throws(TypeError, () => new Temporal.Instant(42), "number");
+assert.throws(TypeError, () => new Temporal.Instant(Symbol()), "symbol");
diff --git a/test/built-ins/Temporal/Instant/basic.js b/test/built-ins/Temporal/Instant/basic.js
new file mode 100644
index 00000000000..2e99e5ef5ea
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/basic.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant
+description: Basic functionality of the Temporal.Instant constructor
+features: [Temporal]
+---*/
+
+const bigIntInstant = new Temporal.Instant(217175010123456789n);
+assert(bigIntInstant instanceof Temporal.Instant, "BigInt instanceof");
+assert.sameValue(bigIntInstant.epochSeconds, 217175010, "BigInt epochSeconds");
+assert.sameValue(bigIntInstant.epochMilliseconds, 217175010123, "BigInt epochMilliseconds");
+
+const stringInstant = new Temporal.Instant("217175010123456789");
+assert(stringInstant instanceof Temporal.Instant, "String instanceof");
+assert.sameValue(stringInstant.epochSeconds, 217175010, "String epochSeconds");
+assert.sameValue(stringInstant.epochMilliseconds, 217175010123, "String epochMilliseconds");
+
+assert.throws(SyntaxError, () => new Temporal.Instant("abc123"), "invalid BigInt syntax");
diff --git a/test/built-ins/Temporal/Instant/builtin.js b/test/built-ins/Temporal/Instant/builtin.js
new file mode 100644
index 00000000000..03f0273f22a
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/builtin.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant
+description: Tests that Temporal.Instant meets the requirements for built-in objects
+info: |
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Instant),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.Instant.prototype,
+ "object", "prototype property");
diff --git a/test/built-ins/Temporal/Instant/compare/argument-zoneddatetime.js b/test/built-ins/Temporal/Instant/compare/argument-zoneddatetime.js
new file mode 100644
index 00000000000..c521b0241cb
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/compare/argument-zoneddatetime.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.compare
+description: Fast path for converting Temporal.ZonedDateTime to Temporal.Instant
+info: |
+ sec-temporal.instant.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalInstant(_one_).
+ 2. Set _two_ to ? ToTemporalInstant(_two_).
+ sec-temporal-totemporalinstant step 1.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return ! CreateTemporalInstant(_item_.[[Nanoseconds]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_000_000_000n);
+
+TemporalHelpers.checkToTemporalInstantFastPath((datetime) => {
+ const result = Temporal.Instant.compare(datetime, instant);
+ assert.sameValue(result, 1, "comparison result");
+});
+
+TemporalHelpers.checkToTemporalInstantFastPath((datetime) => {
+ const result = Temporal.Instant.compare(instant, datetime);
+ assert.sameValue(result, -1, "comparison result");
+});
diff --git a/test/built-ins/Temporal/Instant/compare/builtin.js b/test/built-ins/Temporal/Instant/compare/builtin.js
new file mode 100644
index 00000000000..34b4d20e0d3
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/compare/builtin.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.compare
+description: Tests that Temporal.Instant.compare meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Instant.compare),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.compare),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.compare),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.compare.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Instant/compare/instant-string.js b/test/built-ins/Temporal/Instant/compare/instant-string.js
new file mode 100644
index 00000000000..641585b7429
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/compare/instant-string.js
@@ -0,0 +1,34 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.compare
+description: Conversion of ISO date-time strings to Temporal.Instant instances
+features: [Temporal]
+---*/
+
+const epoch = new Temporal.Instant(0n);
+const hourBefore = new Temporal.Instant(-3600_000_000_000n);
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => Temporal.Instant.compare(str, epoch), "bare date-time string is not an instant (first argument)");
+assert.throws(RangeError, () => Temporal.Instant.compare(epoch, str), "bare date-time string is not an instant (second argument)");
+str = "1970-01-01T00:00[America/Vancouver]";
+assert.throws(RangeError, () => Temporal.Instant.compare(str, epoch), "date-time + IANA annotation is not an instant (first argument)");
+assert.throws(RangeError, () => Temporal.Instant.compare(epoch, str), "date-time + IANA annotation is not an instant (second argument)");
+
+str = "1970-01-01T00:00Z";
+assert.sameValue(Temporal.Instant.compare(str, epoch), 0, "date-time + Z preserves exact time (first argument)");
+assert.sameValue(Temporal.Instant.compare(epoch, str), 0, "date-time + Z preserves exact time (second argument)");
+
+str = "1970-01-01T00:00+01:00";
+assert.sameValue(Temporal.Instant.compare(str, hourBefore), 0, "date-time + offset preserves exact time with offset (first argument)");
+assert.sameValue(Temporal.Instant.compare(hourBefore, str), 0, "date-time + offset preserves exact time with offset (second argument)");
+
+str = "1970-01-01T00:00Z[America/Vancouver]";
+assert.sameValue(Temporal.Instant.compare(str, epoch), 0, "date-time + Z + IANA annotation ignores the IANA annotation (first argument)");
+assert.sameValue(Temporal.Instant.compare(epoch, str), 0, "date-time + Z + IANA annotation ignores the IANA annotation (second argument)");
+
+str = "1970-01-01T00:00+01:00[America/Vancouver]";
+assert.sameValue(Temporal.Instant.compare(str, hourBefore), 0, "date-time + offset + IANA annotation ignores the IANA annotation (first argument)");
+assert.sameValue(Temporal.Instant.compare(hourBefore, str), 0, "date-time + offset + IANA annotation ignores the IANA annotation (second argument)");
diff --git a/test/built-ins/Temporal/Instant/compare/length.js b/test/built-ins/Temporal/Instant/compare/length.js
new file mode 100644
index 00000000000..40ab567e7e4
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/compare/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.compare
+description: Temporal.Instant.compare.length is 2
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.compare, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/compare/name.js b/test/built-ins/Temporal/Instant/compare/name.js
new file mode 100644
index 00000000000..549d71a7519
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/compare/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.compare
+description: Temporal.Instant.compare.name is "compare"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.compare, "name", {
+ value: "compare",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/compare/not-a-constructor.js b/test/built-ins/Temporal/Instant/compare/not-a-constructor.js
new file mode 100644
index 00000000000..169fb1ece47
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/compare/not-a-constructor.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.compare
+description: Temporal.Instant.compare does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Instant.compare();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.compare), false,
+ "isConstructor(Temporal.Instant.compare)");
diff --git a/test/built-ins/Temporal/Instant/compare/prop-desc.js b/test/built-ins/Temporal/Instant/compare/prop-desc.js
new file mode 100644
index 00000000000..6375c4951a5
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/compare/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.compare
+description: The "compare" property of Temporal.Instant
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.compare,
+ "function",
+ "`typeof Instant.compare` is `function`"
+);
+
+verifyProperty(Temporal.Instant, "compare", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/constructor.js b/test/built-ins/Temporal/Instant/constructor.js
new file mode 100644
index 00000000000..ec1d0d7a514
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/constructor.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant
+description: Temporal.Instant constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.Instant());
diff --git a/test/built-ins/Temporal/Instant/from/argument-zoneddatetime.js b/test/built-ins/Temporal/Instant/from/argument-zoneddatetime.js
new file mode 100644
index 00000000000..99fb2042778
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/from/argument-zoneddatetime.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.from
+description: Fast path for converting Temporal.ZonedDateTime to Temporal.Instant
+info: |
+ sec-temporal.instant.from step 2:
+ 2. Return ? ToTemporalInstant(_item_).
+ sec-temporal-totemporalinstant step 1.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return ! CreateTemporalInstant(_item_.[[Nanoseconds]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalInstantFastPath((datetime) => {
+ const result = Temporal.Instant.from(datetime);
+ assert.sameValue(result.epochNanoseconds, result.epochNanoseconds, "epochNanoseconds result");
+});
diff --git a/test/built-ins/Temporal/Instant/from/builtin.js b/test/built-ins/Temporal/Instant/from/builtin.js
new file mode 100644
index 00000000000..c9fb283aec6
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/from/builtin.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.from
+description: Tests that Temporal.Instant.from meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Instant.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.from.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Instant/from/instant-string.js b/test/built-ins/Temporal/Instant/from/instant-string.js
new file mode 100644
index 00000000000..a1131875436
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/from/instant-string.js
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.from
+description: Conversion of ISO date-time strings to Temporal.Instant instances
+features: [Temporal]
+---*/
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => Temporal.Instant.from(str), "bare date-time string is not an instant");
+str = "1970-01-01T00:00[America/Vancouver]";
+assert.throws(RangeError, () => Temporal.Instant.from(str), "date-time + IANA annotation is not an instant");
+
+str = "1970-01-01T00:00Z";
+const result1 = Temporal.Instant.from(str);
+assert.sameValue(result1.epochNanoseconds, 0n, "date-time + Z preserves exact time");
+
+str = "1970-01-01T00:00+01:00";
+const result2 = Temporal.Instant.from(str);
+assert.sameValue(result2.epochNanoseconds, -3600_000_000_000n, "date-time + offset preserves exact time with offset");
+
+str = "1970-01-01T00:00Z[America/Vancouver]";
+const result3 = Temporal.Instant.from(str);
+assert.sameValue(result3.epochNanoseconds, 0n, "date-time + Z + IANA annotation ignores the IANA annotation");
+
+str = "1970-01-01T00:00+01:00[America/Vancouver]";
+const result4 = Temporal.Instant.from(str);
+assert.sameValue(result4.epochNanoseconds, -3600_000_000_000n, "date-time + offset + IANA annotation ignores the IANA annotation");
diff --git a/test/built-ins/Temporal/Instant/from/length.js b/test/built-ins/Temporal/Instant/from/length.js
new file mode 100644
index 00000000000..80c58fff0c3
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/from/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.from
+description: Temporal.Instant.from.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/from/name.js b/test/built-ins/Temporal/Instant/from/name.js
new file mode 100644
index 00000000000..37e951c9da6
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/from/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.from
+description: Temporal.Instant.from.name is "from"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/from/not-a-constructor.js b/test/built-ins/Temporal/Instant/from/not-a-constructor.js
new file mode 100644
index 00000000000..a508a14d1c6
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/from/not-a-constructor.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.from
+description: Temporal.Instant.from does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Instant.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.from), false,
+ "isConstructor(Temporal.Instant.from)");
diff --git a/test/built-ins/Temporal/Instant/from/prop-desc.js b/test/built-ins/Temporal/Instant/from/prop-desc.js
new file mode 100644
index 00000000000..ef65d799d41
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/from/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.from
+description: The "from" property of Temporal.Instant
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.from,
+ "function",
+ "`typeof Instant.from` is `function`"
+);
+
+verifyProperty(Temporal.Instant, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/from/subclassing-ignored.js b/test/built-ins/Temporal/Instant/from/subclassing-ignored.js
new file mode 100644
index 00000000000..46593de5b36
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/from/subclassing-ignored.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.Instant,
+ "from",
+ ["1976-11-18T14:23:30.123456789Z"],
+ (result) => assert.sameValue(result.epochNanoseconds, 217_175_010_123_456_789n, "epochNanoseconds result"),
+);
diff --git a/test/built-ins/Temporal/Instant/from/timezone-custom.js b/test/built-ins/Temporal/Instant/from/timezone-custom.js
new file mode 100644
index 00000000000..6063d6f5c85
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/from/timezone-custom.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.from
+description: Time zone annotation is ignored in input ISO string
+features: [Temporal]
+---*/
+
+const dateTimeString = "1975-02-02T14:25:36.123456789";
+
+Object.defineProperty(Temporal.TimeZone, "from", {
+ get() {
+ throw new Test262Error("should not get Temporal.TimeZone.from");
+ },
+});
+
+const instant = Temporal.Instant.from(dateTimeString + "+01:00[Custom/TimeZone]");
+assert.sameValue(instant.epochMilliseconds, 160579536123);
diff --git a/test/built-ins/Temporal/Instant/fromEpochMicroseconds/basic.js b/test/built-ins/Temporal/Instant/fromEpochMicroseconds/basic.js
new file mode 100644
index 00000000000..73ffb79e098
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochMicroseconds/basic.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochmicroseconds
+description: Basic tests for Instant.fromEpochMicroseconds().
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = Temporal.Instant.fromEpochMicroseconds(217175010_123_456n);
+assert.sameValue(afterEpoch.epochNanoseconds, 217175010_123_456_000n, "fromEpochMicroseconds post epoch");
+
+const beforeEpoch = Temporal.Instant.fromEpochMicroseconds(-217175010_876_543n);
+assert.sameValue(beforeEpoch.epochNanoseconds, -217175010_876_543_000n, "fromEpochMicroseconds pre epoch");
diff --git a/test/built-ins/Temporal/Instant/fromEpochMicroseconds/builtin.js b/test/built-ins/Temporal/Instant/fromEpochMicroseconds/builtin.js
new file mode 100644
index 00000000000..073b7f21b23
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochMicroseconds/builtin.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochmicroseconds
+description: Tests that Temporal.Instant.fromEpochMicroseconds meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Instant.fromEpochMicroseconds),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.fromEpochMicroseconds),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.fromEpochMicroseconds),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.fromEpochMicroseconds.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Instant/fromEpochMicroseconds/length.js b/test/built-ins/Temporal/Instant/fromEpochMicroseconds/length.js
new file mode 100644
index 00000000000..a5bb9922d30
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochMicroseconds/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochmicroseconds
+description: Temporal.Instant.fromEpochMicroseconds.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.fromEpochMicroseconds, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/fromEpochMicroseconds/name.js b/test/built-ins/Temporal/Instant/fromEpochMicroseconds/name.js
new file mode 100644
index 00000000000..4ed946db311
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochMicroseconds/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochmicroseconds
+description: Temporal.Instant.fromEpochMicroseconds.name is "fromEpochMicroseconds"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.fromEpochMicroseconds, "name", {
+ value: "fromEpochMicroseconds",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/fromEpochMicroseconds/not-a-constructor.js b/test/built-ins/Temporal/Instant/fromEpochMicroseconds/not-a-constructor.js
new file mode 100644
index 00000000000..7bb9615fc58
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochMicroseconds/not-a-constructor.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochmicroseconds
+description: Temporal.Instant.fromEpochMicroseconds does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Instant.fromEpochMicroseconds();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.fromEpochMicroseconds), false,
+ "isConstructor(Temporal.Instant.fromEpochMicroseconds)");
diff --git a/test/built-ins/Temporal/Instant/fromEpochMicroseconds/prop-desc.js b/test/built-ins/Temporal/Instant/fromEpochMicroseconds/prop-desc.js
new file mode 100644
index 00000000000..bdb25776b44
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochMicroseconds/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochmicroseconds
+description: The "fromEpochMicroseconds" property of Temporal.Instant
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.fromEpochMicroseconds,
+ "function",
+ "`typeof Instant.fromEpochMicroseconds` is `function`"
+);
+
+verifyProperty(Temporal.Instant, "fromEpochMicroseconds", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/fromEpochMicroseconds/subclassing-ignored.js b/test/built-ins/Temporal/Instant/fromEpochMicroseconds/subclassing-ignored.js
new file mode 100644
index 00000000000..b2542670ac1
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochMicroseconds/subclassing-ignored.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochmicroseconds
+description: The receiver is never called by fromEpochMicroseconds()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.Instant,
+ "fromEpochMicroseconds",
+ [10n],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 10_000n, "epochNanoseconds result");
+ },
+);
diff --git a/test/built-ins/Temporal/Instant/fromEpochMilliseconds/basic.js b/test/built-ins/Temporal/Instant/fromEpochMilliseconds/basic.js
new file mode 100644
index 00000000000..2da12ffb4fd
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochMilliseconds/basic.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochmilliseconds
+description: Basic tests for Instant.fromEpochMilliseconds().
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = Temporal.Instant.fromEpochMilliseconds(217175010_123);
+assert.sameValue(afterEpoch.epochNanoseconds, 217175010_123_000_000n, "fromEpochMilliseconds post epoch");
+
+const beforeEpoch = Temporal.Instant.fromEpochMilliseconds(-217175010_876);
+assert.sameValue(beforeEpoch.epochNanoseconds, -217175010_876_000_000n, "fromEpochMilliseconds pre epoch");
diff --git a/test/built-ins/Temporal/Instant/fromEpochMilliseconds/builtin.js b/test/built-ins/Temporal/Instant/fromEpochMilliseconds/builtin.js
new file mode 100644
index 00000000000..a081407a9f6
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochMilliseconds/builtin.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochmilliseconds
+description: Tests that Temporal.Instant.fromEpochMilliseconds meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Instant.fromEpochMilliseconds),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.fromEpochMilliseconds),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.fromEpochMilliseconds),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.fromEpochMilliseconds.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Instant/fromEpochMilliseconds/length.js b/test/built-ins/Temporal/Instant/fromEpochMilliseconds/length.js
new file mode 100644
index 00000000000..a1186693ad6
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochMilliseconds/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochmilliseconds
+description: Temporal.Instant.fromEpochMilliseconds.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.fromEpochMilliseconds, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/fromEpochMilliseconds/name.js b/test/built-ins/Temporal/Instant/fromEpochMilliseconds/name.js
new file mode 100644
index 00000000000..1d3cc439fb7
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochMilliseconds/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochmilliseconds
+description: Temporal.Instant.fromEpochMilliseconds.name is "fromEpochMilliseconds"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.fromEpochMilliseconds, "name", {
+ value: "fromEpochMilliseconds",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/fromEpochMilliseconds/not-a-constructor.js b/test/built-ins/Temporal/Instant/fromEpochMilliseconds/not-a-constructor.js
new file mode 100644
index 00000000000..58e8573b1bf
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochMilliseconds/not-a-constructor.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochmilliseconds
+description: Temporal.Instant.fromEpochMilliseconds does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Instant.fromEpochMilliseconds();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.fromEpochMilliseconds), false,
+ "isConstructor(Temporal.Instant.fromEpochMilliseconds)");
diff --git a/test/built-ins/Temporal/Instant/fromEpochMilliseconds/prop-desc.js b/test/built-ins/Temporal/Instant/fromEpochMilliseconds/prop-desc.js
new file mode 100644
index 00000000000..378a91e5b21
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochMilliseconds/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochmilliseconds
+description: The "fromEpochMilliseconds" property of Temporal.Instant
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.fromEpochMilliseconds,
+ "function",
+ "`typeof Instant.fromEpochMilliseconds` is `function`"
+);
+
+verifyProperty(Temporal.Instant, "fromEpochMilliseconds", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/fromEpochMilliseconds/subclassing-ignored.js b/test/built-ins/Temporal/Instant/fromEpochMilliseconds/subclassing-ignored.js
new file mode 100644
index 00000000000..6ea5bf1fd19
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochMilliseconds/subclassing-ignored.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochmilliseconds
+description: The receiver is never called by fromEpochMilliseconds()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.Instant,
+ "fromEpochMilliseconds",
+ [10],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 10_000_000n, "epochNanoseconds result");
+ },
+);
diff --git a/test/built-ins/Temporal/Instant/fromEpochNanoseconds/basic.js b/test/built-ins/Temporal/Instant/fromEpochNanoseconds/basic.js
new file mode 100644
index 00000000000..229c408940d
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochNanoseconds/basic.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochnanoseconds
+description: Basic tests for Instant.fromEpochNanoseconds().
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = Temporal.Instant.fromEpochNanoseconds(217175010_123_456_789n);
+assert.sameValue(afterEpoch.epochNanoseconds, 217175010_123_456_789n, "fromEpochNanoseconds post epoch");
+
+const beforeEpoch = Temporal.Instant.fromEpochNanoseconds(-217175010_876_543_211n);
+assert.sameValue(beforeEpoch.epochNanoseconds, -217175010_876_543_211n, "fromEpochNanoseconds pre epoch");
diff --git a/test/built-ins/Temporal/Instant/fromEpochNanoseconds/builtin.js b/test/built-ins/Temporal/Instant/fromEpochNanoseconds/builtin.js
new file mode 100644
index 00000000000..c07dae9f1c8
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochNanoseconds/builtin.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochnanoseconds
+description: Tests that Temporal.Instant.fromEpochNanoseconds meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Instant.fromEpochNanoseconds),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.fromEpochNanoseconds),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.fromEpochNanoseconds),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.fromEpochNanoseconds.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Instant/fromEpochNanoseconds/length.js b/test/built-ins/Temporal/Instant/fromEpochNanoseconds/length.js
new file mode 100644
index 00000000000..7168cc80c0f
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochNanoseconds/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochnanoseconds
+description: Temporal.Instant.fromEpochNanoseconds.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.fromEpochNanoseconds, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/fromEpochNanoseconds/name.js b/test/built-ins/Temporal/Instant/fromEpochNanoseconds/name.js
new file mode 100644
index 00000000000..94235368785
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochNanoseconds/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochnanoseconds
+description: Temporal.Instant.fromEpochNanoseconds.name is "fromEpochNanoseconds"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.fromEpochNanoseconds, "name", {
+ value: "fromEpochNanoseconds",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/fromEpochNanoseconds/not-a-constructor.js b/test/built-ins/Temporal/Instant/fromEpochNanoseconds/not-a-constructor.js
new file mode 100644
index 00000000000..d265ebf3f87
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochNanoseconds/not-a-constructor.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochnanoseconds
+description: Temporal.Instant.fromEpochNanoseconds does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Instant.fromEpochNanoseconds();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.fromEpochNanoseconds), false,
+ "isConstructor(Temporal.Instant.fromEpochNanoseconds)");
diff --git a/test/built-ins/Temporal/Instant/fromEpochNanoseconds/prop-desc.js b/test/built-ins/Temporal/Instant/fromEpochNanoseconds/prop-desc.js
new file mode 100644
index 00000000000..8a5e4146448
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochNanoseconds/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochnanoseconds
+description: The "fromEpochNanoseconds" property of Temporal.Instant
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.fromEpochNanoseconds,
+ "function",
+ "`typeof Instant.fromEpochNanoseconds` is `function`"
+);
+
+verifyProperty(Temporal.Instant, "fromEpochNanoseconds", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/fromEpochNanoseconds/subclassing-ignored.js b/test/built-ins/Temporal/Instant/fromEpochNanoseconds/subclassing-ignored.js
new file mode 100644
index 00000000000..9cda20d6fe0
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochNanoseconds/subclassing-ignored.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochnanoseconds
+description: The receiver is never called by fromEpochNanoseconds()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.Instant,
+ "fromEpochNanoseconds",
+ [10n],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 10n, "epochNanoseconds result");
+ },
+);
diff --git a/test/built-ins/Temporal/Instant/fromEpochSeconds/basic.js b/test/built-ins/Temporal/Instant/fromEpochSeconds/basic.js
new file mode 100644
index 00000000000..ad9f1756605
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochSeconds/basic.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochseconds
+description: Basic tests for Instant.fromEpochSeconds().
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = Temporal.Instant.fromEpochSeconds(217175010);
+assert.sameValue(afterEpoch.epochNanoseconds, 217175010_000_000_000n, "fromEpochSeconds post epoch");
+
+const beforeEpoch = Temporal.Instant.fromEpochSeconds(-217175010);
+assert.sameValue(beforeEpoch.epochNanoseconds, -217175010_000_000_000n, "fromEpochSeconds pre epoch");
diff --git a/test/built-ins/Temporal/Instant/fromEpochSeconds/builtin.js b/test/built-ins/Temporal/Instant/fromEpochSeconds/builtin.js
new file mode 100644
index 00000000000..6fba9b72550
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochSeconds/builtin.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochseconds
+description: Tests that Temporal.Instant.fromEpochSeconds meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Instant.fromEpochSeconds),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.fromEpochSeconds),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.fromEpochSeconds),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.fromEpochSeconds.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Instant/fromEpochSeconds/length.js b/test/built-ins/Temporal/Instant/fromEpochSeconds/length.js
new file mode 100644
index 00000000000..5d0a1b1d796
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochSeconds/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochseconds
+description: Temporal.Instant.fromEpochSeconds.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.fromEpochSeconds, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/fromEpochSeconds/name.js b/test/built-ins/Temporal/Instant/fromEpochSeconds/name.js
new file mode 100644
index 00000000000..75720d62940
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochSeconds/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochseconds
+description: Temporal.Instant.fromEpochSeconds.name is "fromEpochSeconds"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.fromEpochSeconds, "name", {
+ value: "fromEpochSeconds",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/fromEpochSeconds/not-a-constructor.js b/test/built-ins/Temporal/Instant/fromEpochSeconds/not-a-constructor.js
new file mode 100644
index 00000000000..800103e6380
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochSeconds/not-a-constructor.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochseconds
+description: Temporal.Instant.fromEpochSeconds does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Instant.fromEpochSeconds();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.fromEpochSeconds), false,
+ "isConstructor(Temporal.Instant.fromEpochSeconds)");
diff --git a/test/built-ins/Temporal/Instant/fromEpochSeconds/prop-desc.js b/test/built-ins/Temporal/Instant/fromEpochSeconds/prop-desc.js
new file mode 100644
index 00000000000..48d51e344d3
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochSeconds/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochseconds
+description: The "fromEpochSeconds" property of Temporal.Instant
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.fromEpochSeconds,
+ "function",
+ "`typeof Instant.fromEpochSeconds` is `function`"
+);
+
+verifyProperty(Temporal.Instant, "fromEpochSeconds", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/fromEpochSeconds/subclassing-ignored.js b/test/built-ins/Temporal/Instant/fromEpochSeconds/subclassing-ignored.js
new file mode 100644
index 00000000000..334950198ba
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/fromEpochSeconds/subclassing-ignored.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.fromepochseconds
+description: The receiver is never called by fromEpochSeconds()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.Instant,
+ "fromEpochSeconds",
+ [10],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 10_000_000_000n, "epochNanoseconds result");
+ },
+);
diff --git a/test/built-ins/Temporal/Instant/length.js b/test/built-ins/Temporal/Instant/length.js
new file mode 100644
index 00000000000..7e05ca7a049
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant
+description: Temporal.Instant.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/name.js b/test/built-ins/Temporal/Instant/name.js
new file mode 100644
index 00000000000..03f0f41413d
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant
+description: Temporal.Instant.name is "Instant"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant, "name", {
+ value: "Instant",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prop-desc.js b/test/built-ins/Temporal/Instant/prop-desc.js
new file mode 100644
index 00000000000..e2afc93926f
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant
+description: The "Instant" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant,
+ "function",
+ "`typeof Instant` is `function`"
+);
+
+verifyProperty(Temporal, "Instant", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/add/argument-string-negative-fractional-units.js b/test/built-ins/Temporal/Instant/prototype/add/argument-string-negative-fractional-units.js
new file mode 100644
index 00000000000..9fee0fa701f
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/add/argument-string-negative-fractional-units.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.add
+description: Strings with fractional duration units are treated with the correct sign
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
+
+const resultHours = instance.add("-PT24.567890123H");
+assert.sameValue(resultHours.epochNanoseconds, 999_911_555_595_557_201n, "negative fractional hours");
+
+const resultMinutes = instance.add("-PT1440.567890123M");
+assert.sameValue(resultMinutes.epochNanoseconds, 999_913_565_926_592_621n, "negative fractional minutes");
diff --git a/test/built-ins/Temporal/Instant/prototype/add/argument-string.js b/test/built-ins/Temporal/Instant/prototype/add/argument-string.js
new file mode 100644
index 00000000000..3dec39fc634
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/add/argument-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.add
+description: A string is parsed into the correct object when passed as the argument
+features: [Temporal]
+---*/
+
+const instance = Temporal.Instant.fromEpochSeconds(10);
+const result = instance.add("PT3H");
+assert.sameValue(result.epochNanoseconds, 10_810_000_000_000n, "epochNanoseconds result");
diff --git a/test/built-ins/Temporal/Instant/prototype/add/builtin.js b/test/built-ins/Temporal/Instant/prototype/add/builtin.js
new file mode 100644
index 00000000000..a7925112289
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/add/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.add
+description: >
+ Tests that Temporal.Instant.prototype.add
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Instant.prototype.add),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.add),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.add),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.add.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Instant/prototype/add/infinity-throws-rangeerror.js b/test/built-ins/Temporal/Instant/prototype/add/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..2f75efe04cd
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/add/infinity-throws-rangeerror.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.Instant.prototype.add throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.instant.prototype.add
+features: [Temporal]
+---*/
+
+const fields = ["hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.Instant.fromEpochSeconds(10);
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: Infinity }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/add/length.js b/test/built-ins/Temporal/Instant/prototype/add/length.js
new file mode 100644
index 00000000000..dbc005d39bb
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/add/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.add
+description: Temporal.Instant.prototype.add.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.add, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/add/name.js b/test/built-ins/Temporal/Instant/prototype/add/name.js
new file mode 100644
index 00000000000..ebff49cb698
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/add/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.add
+description: Temporal.Instant.prototype.add.name is "add".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.add, "name", {
+ value: "add",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/add/negative-infinity-throws-rangeerror.js b/test/built-ins/Temporal/Instant/prototype/add/negative-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..57d3bb46e2f
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/add/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.Instant.prototype.add throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.instant.prototype.add
+features: [Temporal]
+---*/
+
+const fields = ["hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.Instant.fromEpochSeconds(10);
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: -Infinity }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/add/non-integer-throws-rangeerror.js b/test/built-ins/Temporal/Instant/prototype/add/non-integer-throws-rangeerror.js
new file mode 100644
index 00000000000..2406115474f
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/add/non-integer-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.add
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.add({ [field]: -1.5 }));
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/add/not-a-constructor.js b/test/built-ins/Temporal/Instant/prototype/add/not-a-constructor.js
new file mode 100644
index 00000000000..8c5e702b8fa
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/add/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.add
+description: >
+ Temporal.Instant.prototype.add does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Instant.prototype.add();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.add), false,
+ "isConstructor(Temporal.Instant.prototype.add)");
diff --git a/test/built-ins/Temporal/Instant/prototype/add/order-of-operations.js b/test/built-ins/Temporal/Instant/prototype/add/order-of-operations.js
new file mode 100644
index 00000000000..aec517d484c
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/add/order-of-operations.js
@@ -0,0 +1,61 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.add
+description: Properties on an object passed to add() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(10n);
+const expected = [
+ "get days",
+ "get hours",
+ "get hours.valueOf",
+ "call hours.valueOf",
+ "get microseconds",
+ "get microseconds.valueOf",
+ "call microseconds.valueOf",
+ "get milliseconds",
+ "get milliseconds.valueOf",
+ "call milliseconds.valueOf",
+ "get minutes",
+ "get minutes.valueOf",
+ "call minutes.valueOf",
+ "get months",
+ "get nanoseconds",
+ "get nanoseconds.valueOf",
+ "call nanoseconds.valueOf",
+ "get seconds",
+ "get seconds.valueOf",
+ "call seconds.valueOf",
+ "get weeks",
+ "get years",
+];
+const actual = [];
+const fields = {
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = instance.add(argument);
+assert.sameValue(result.epochNanoseconds, 3661001001011n, "epochNanoseconds result");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/Instant/prototype/add/prop-desc.js b/test/built-ins/Temporal/Instant/prototype/add/prop-desc.js
new file mode 100644
index 00000000000..8860a79d829
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/add/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.add
+description: The "add" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.add,
+ "function",
+ "`typeof Instant.prototype.add` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "add", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/add/result-out-of-range.js b/test/built-ins/Temporal/Instant/prototype/add/result-out-of-range.js
new file mode 100644
index 00000000000..63f6bba22a3
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/add/result-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.add
+description: RangeError thrown if result is outside representable range
+features: [Temporal]
+---*/
+
+const fields = ["hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.Instant.fromEpochNanoseconds(8640000_000_000_000_000_000n);
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: 1 }));
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/add/subclassing-ignored.js b/test/built-ins/Temporal/Instant/prototype/add/subclassing-ignored.js
new file mode 100644
index 00000000000..62bd095bc53
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/add/subclassing-ignored.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.add
+description: Objects of a subclass are never created as return values for add()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.Instant,
+ [10n],
+ "add",
+ [{ nanoseconds: 5 }],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 15n, "epochNanoseconds result");
+ },
+);
diff --git a/test/built-ins/Temporal/Instant/prototype/builtin.js b/test/built-ins/Temporal/Instant/prototype/builtin.js
new file mode 100644
index 00000000000..566eb66cc23
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/builtin.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-instant-objects
+description: Temporal.Instant.prototype meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+const { Instant } = Temporal;
+
+assert.sameValue(Object.isExtensible(Instant.prototype), true,
+ "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Instant.prototype),
+ "[object Temporal.Instant]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Instant.prototype), Object.prototype,
+ "Built-in prototype objects must have Object.prototype as their prototype.");
+
+assert.sameValue(Instant.prototype.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Instant/prototype/epochMicroseconds/basic.js b/test/built-ins/Temporal/Instant/prototype/epochMicroseconds/basic.js
new file mode 100644
index 00000000000..ca4a582ab60
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/epochMicroseconds/basic.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.instant.prototype.epochmicroseconds
+description: Basic tests for epochMicroseconds.
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = new Temporal.Instant(217175010_123_456_789n);
+assert.sameValue(afterEpoch.epochMicroseconds, 217175010_123_456n, "epochMicroseconds post epoch");
+assert.sameValue(typeof afterEpoch.epochMicroseconds, "bigint", "epochMicroseconds value is a bigint");
+
+const beforeEpoch = new Temporal.Instant(-217175010_876_543_211n);
+assert.sameValue(beforeEpoch.epochMicroseconds, -217175010_876_543n, "epochMicroseconds pre epoch");
+assert.sameValue(typeof beforeEpoch.epochMicroseconds, "bigint", "epochMicroseconds value is a bigint");
diff --git a/test/built-ins/Temporal/Instant/prototype/epochMicroseconds/prop-desc.js b/test/built-ins/Temporal/Instant/prototype/epochMicroseconds/prop-desc.js
new file mode 100644
index 00000000000..c64fe279727
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/epochMicroseconds/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.instant.prototype.epochmicroseconds
+description: The "epochMicroseconds" property of Temporal.Instant.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Instant.prototype, "epochMicroseconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/Instant/prototype/epochMilliseconds/basic.js b/test/built-ins/Temporal/Instant/prototype/epochMilliseconds/basic.js
new file mode 100644
index 00000000000..418a5a95e4a
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/epochMilliseconds/basic.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.instant.prototype.epochmilliseconds
+description: Basic tests for epochMilliseconds.
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = new Temporal.Instant(217175010_123_456_789n);
+assert.sameValue(afterEpoch.epochMilliseconds, 217175010_123, "epochMilliseconds post epoch");
+assert.sameValue(typeof afterEpoch.epochMilliseconds, "number", "epochMilliseconds value is a number");
+
+const beforeEpoch = new Temporal.Instant(-217175010_876_543_211n);
+assert.sameValue(beforeEpoch.epochMilliseconds, -217175010_876, "epochMilliseconds pre epoch");
+assert.sameValue(typeof beforeEpoch.epochMilliseconds, "number", "epochMilliseconds value is a number");
diff --git a/test/built-ins/Temporal/Instant/prototype/epochMilliseconds/prop-desc.js b/test/built-ins/Temporal/Instant/prototype/epochMilliseconds/prop-desc.js
new file mode 100644
index 00000000000..e4370f4ecba
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/epochMilliseconds/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.instant.prototype.epochmilliseconds
+description: The "epochMilliseconds" property of Temporal.Instant.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Instant.prototype, "epochMilliseconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/Instant/prototype/epochNanoseconds/basic.js b/test/built-ins/Temporal/Instant/prototype/epochNanoseconds/basic.js
new file mode 100644
index 00000000000..dcbbfe4aabe
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/epochNanoseconds/basic.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.instant.prototype.epochnanoseconds
+description: Basic tests for epochNanoseconds.
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = new Temporal.Instant(217175010_123_456_789n);
+assert.sameValue(afterEpoch.epochNanoseconds, 217175010_123_456_789n, "epochNanoseconds post epoch");
+assert.sameValue(typeof afterEpoch.epochNanoseconds, "bigint", "epochNanoseconds value is a bigint");
+
+const beforeEpoch = new Temporal.Instant(-217175010_876_543_211n);
+assert.sameValue(beforeEpoch.epochNanoseconds, -217175010_876_543_211n, "epochNanoseconds pre epoch");
+assert.sameValue(typeof beforeEpoch.epochNanoseconds, "bigint", "epochNanoseconds value is a bigint");
diff --git a/test/built-ins/Temporal/Instant/prototype/epochNanoseconds/prop-desc.js b/test/built-ins/Temporal/Instant/prototype/epochNanoseconds/prop-desc.js
new file mode 100644
index 00000000000..dd1c655c7de
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/epochNanoseconds/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.instant.prototype.epochnanoseconds
+description: The "epochNanoseconds" property of Temporal.Instant.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Instant.prototype, "epochNanoseconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/Instant/prototype/epochSeconds/basic.js b/test/built-ins/Temporal/Instant/prototype/epochSeconds/basic.js
new file mode 100644
index 00000000000..7c3e8ab7987
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/epochSeconds/basic.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.instant.prototype.epochseconds
+description: Basic tests for epochSeconds.
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = new Temporal.Instant(217175010_123_456_789n);
+assert.sameValue(afterEpoch.epochSeconds, 217175010, "epochSeconds post epoch");
+assert.sameValue(typeof afterEpoch.epochSeconds, "number", "epochSeconds value is a number");
+
+const beforeEpoch = new Temporal.Instant(-217175010_876_543_211n);
+assert.sameValue(beforeEpoch.epochSeconds, -217175010, "epochSeconds pre epoch");
+assert.sameValue(typeof beforeEpoch.epochSeconds, "number", "epochSeconds value is a number");
diff --git a/test/built-ins/Temporal/Instant/prototype/epochSeconds/prop-desc.js b/test/built-ins/Temporal/Instant/prototype/epochSeconds/prop-desc.js
new file mode 100644
index 00000000000..2b92c7f65bb
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/epochSeconds/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.instant.prototype.epochseconds
+description: The "epochSeconds" property of Temporal.Instant.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.Instant.prototype, "epochSeconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/Instant/prototype/equals/argument-wrong-type.js b/test/built-ins/Temporal/Instant/prototype/equals/argument-wrong-type.js
new file mode 100644
index 00000000000..04d3d188bac
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/equals/argument-wrong-type.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.equals
+description: Appropriate error thrown when argument cannot be converted to a valid string
+features: [Symbol, Temporal]
+---*/
+
+const instance = Temporal.Instant.fromEpochSeconds(0);
+
+assert.throws(RangeError, () => instance.equals(undefined), "undefined");
+assert.throws(RangeError, () => instance.equals(null), "null");
+assert.throws(RangeError, () => instance.equals(true), "true");
+assert.throws(RangeError, () => instance.equals(""), "empty string");
+assert.throws(TypeError, () => instance.equals(Symbol()), "symbol");
+assert.throws(RangeError, () => instance.equals(1), "1");
+assert.throws(RangeError, () => instance.equals({}), "plain object");
+assert.throws(RangeError, () => instance.equals(Temporal.Instant), "Temporal.Instant");
+assert.throws(TypeError, () => instance.equals(Temporal.Instant.prototype), "Temporal.Instant.prototype");
diff --git a/test/built-ins/Temporal/Instant/prototype/equals/argument-zoneddatetime.js b/test/built-ins/Temporal/Instant/prototype/equals/argument-zoneddatetime.js
new file mode 100644
index 00000000000..20e299ca7e6
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/equals/argument-zoneddatetime.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: Fast path for converting Temporal.ZonedDateTime to Temporal.Instant
+info: |
+ sec-temporal.instant.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalInstant(_other_).
+ sec-temporal-totemporalinstant step 1.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return ! CreateTemporalInstant(_item_.[[Nanoseconds]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalInstantFastPath((datetime) => {
+ const instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ assert(instant.equals(datetime));
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/equals/builtin.js b/test/built-ins/Temporal/Instant/prototype/equals/builtin.js
new file mode 100644
index 00000000000..a0e0945d45a
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/equals/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.equals
+description: >
+ Tests that Temporal.Instant.prototype.equals
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Instant.prototype.equals),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.equals),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.equals),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.equals.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Instant/prototype/equals/instant-string.js b/test/built-ins/Temporal/Instant/prototype/equals/instant-string.js
new file mode 100644
index 00000000000..b3924fe83db
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/equals/instant-string.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.equals
+description: Conversion of ISO date-time strings to Temporal.Instant instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.equals(str), "bare date-time string is not an instant");
+str = "1970-01-01T00:00[America/Vancouver]";
+assert.throws(RangeError, () => instance.equals(str), "date-time + IANA annotation is not an instant");
+
+str = "1970-01-01T00:00Z";
+const result1 = instance.equals(str);
+assert.sameValue(result1, true, "date-time + Z preserves exact time");
+
+str = "1970-01-01T00:00+01:00";
+const result2 = instance.equals(str);
+assert.sameValue(result2, false, "date-time + offset preserves exact time with offset");
+
+str = "1970-01-01T00:00Z[America/Vancouver]";
+const result3 = instance.equals(str);
+assert.sameValue(result3, true, "date-time + Z + IANA annotation ignores the IANA annotation");
+
+str = "1970-01-01T00:00+01:00[America/Vancouver]";
+const result4 = instance.equals(str);
+assert.sameValue(result4, false, "date-time + offset + IANA annotation ignores the IANA annotation");
diff --git a/test/built-ins/Temporal/Instant/prototype/equals/length.js b/test/built-ins/Temporal/Instant/prototype/equals/length.js
new file mode 100644
index 00000000000..82069609570
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/equals/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.equals
+description: Temporal.Instant.prototype.equals.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.equals, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/equals/name.js b/test/built-ins/Temporal/Instant/prototype/equals/name.js
new file mode 100644
index 00000000000..e1c3d7c7d11
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/equals/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.equals
+description: Temporal.Instant.prototype.equals.name is "equals".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.equals, "name", {
+ value: "equals",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/equals/not-a-constructor.js b/test/built-ins/Temporal/Instant/prototype/equals/not-a-constructor.js
new file mode 100644
index 00000000000..db2b3293dc3
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/equals/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.equals
+description: >
+ Temporal.Instant.prototype.equals does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Instant.prototype.equals();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.equals), false,
+ "isConstructor(Temporal.Instant.prototype.equals)");
diff --git a/test/built-ins/Temporal/Instant/prototype/equals/prop-desc.js b/test/built-ins/Temporal/Instant/prototype/equals/prop-desc.js
new file mode 100644
index 00000000000..c34110ba215
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/equals/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.equals
+description: The "equals" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.equals,
+ "function",
+ "`typeof Instant.prototype.equals` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "equals", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/prop-desc.js b/test/built-ins/Temporal/Instant/prototype/prop-desc.js
new file mode 100644
index 00000000000..69c73f59308
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/prop-desc.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-instant-objects
+description: The "prototype" property of Temporal.Instant
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+const { Instant } = Temporal;
+
+assert.sameValue(typeof Instant.prototype, "object");
+assert.notSameValue(Instant.prototype, null);
+
+verifyProperty(Instant, "prototype", {
+ writable: false,
+ enumerable: false,
+ configurable: false,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/round/builtin.js b/test/built-ins/Temporal/Instant/prototype/round/builtin.js
new file mode 100644
index 00000000000..42779f41800
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/round/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.round
+description: >
+ Tests that Temporal.Instant.prototype.round
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Instant.prototype.round),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.round),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.round),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.round.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Instant/prototype/round/length.js b/test/built-ins/Temporal/Instant/prototype/round/length.js
new file mode 100644
index 00000000000..1be5bec8c91
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/round/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.round
+description: Temporal.Instant.prototype.round.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.round, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/round/name.js b/test/built-ins/Temporal/Instant/prototype/round/name.js
new file mode 100644
index 00000000000..9d1f03e7c69
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/round/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.round
+description: Temporal.Instant.prototype.round.name is "round".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.round, "name", {
+ value: "round",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/round/not-a-constructor.js b/test/built-ins/Temporal/Instant/prototype/round/not-a-constructor.js
new file mode 100644
index 00000000000..eec9f335865
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/round/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.round
+description: >
+ Temporal.Instant.prototype.round does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Instant.prototype.round();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.round), false,
+ "isConstructor(Temporal.Instant.prototype.round)");
diff --git a/test/built-ins/Temporal/Instant/prototype/round/options-wrong-type.js b/test/built-ins/Temporal/Instant/prototype/round/options-wrong-type.js
new file mode 100644
index 00000000000..455401e261b
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/round/options-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.round
+description: TypeError thrown when options argument is missing or a primitive
+features: [Symbol, Temporal]
+---*/
+
+const values = [
+ undefined,
+ null,
+ true,
+ "string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.Instant(0n);
+assert.throws(TypeError, () => instance.round(), "missing argument");
+for (const value of values) {
+ assert.throws(TypeError, () => instance.round(value), `argument ${String(value)}`);
+}
diff --git a/test/built-ins/Temporal/Instant/prototype/round/prop-desc.js b/test/built-ins/Temporal/Instant/prototype/round/prop-desc.js
new file mode 100644
index 00000000000..fda6d47cd3a
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/round/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.round
+description: The "round" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.round,
+ "function",
+ "`typeof Instant.prototype.round` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "round", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/round/roundingincrement-nan.js b/test/built-ins/Temporal/Instant/prototype/round/roundingincrement-nan.js
new file mode 100644
index 00000000000..d76a859a9ad
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/round/roundingincrement-nan.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.round
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.instant.prototype.round step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *true*).
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+assert.throws(RangeError, () => instant.round({ smallestUnit: 'second', roundingIncrement: NaN }));
diff --git a/test/built-ins/Temporal/Instant/prototype/round/roundingincrement-non-integer.js b/test/built-ins/Temporal/Instant/prototype/round/roundingincrement-non-integer.js
new file mode 100644
index 00000000000..859b767b10b
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/round/roundingincrement-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.round
+description: Rounding for roundingIncrement option
+info: |
+ sec-temporal-totemporalroundingincrement step 7:
+ 7. Set _increment_ to floor(ℝ(_increment_)).
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_000_000_005n);
+const result = instant.round({ smallestUnit: "nanosecond", roundingIncrement: 2.5 });
+assert.sameValue(result.epochNanoseconds, 1_000_000_000_000_000_006n, "roundingIncrement 2.5 floors to 2");
diff --git a/test/built-ins/Temporal/Instant/prototype/round/roundingincrement-out-of-range.js b/test/built-ins/Temporal/Instant/prototype/round/roundingincrement-out-of-range.js
new file mode 100644
index 00000000000..93f3e3273af
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/round/roundingincrement-out-of-range.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.round
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ sec-temporal-totemporalroundingincrement step 6:
+ 6. If _increment_ < 1 or _increment_ > _maximum_, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_000_000_005n);
+assert.throws(RangeError, () => instant.round({ smallestUnit: "nanoseconds", roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => instant.round({ smallestUnit: "nanoseconds", roundingIncrement: -1 }));
+assert.throws(RangeError, () => instant.round({ smallestUnit: "nanoseconds", roundingIncrement: 0 }));
+assert.throws(RangeError, () => instant.round({ smallestUnit: "nanoseconds", roundingIncrement: Infinity }));
diff --git a/test/built-ins/Temporal/Instant/prototype/round/roundingincrement-undefined.js b/test/built-ins/Temporal/Instant/prototype/round/roundingincrement-undefined.js
new file mode 100644
index 00000000000..918b377243e
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/round/roundingincrement-undefined.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.round
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.instant.prototype.round step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *true*).
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+
+const explicit = instant.round({ smallestUnit: 'second', roundingIncrement: undefined });
+assert.sameValue(explicit.epochNanoseconds, 1_000_000_001_000_000_000n, "default roundingIncrement is 1");
+
+const implicit = instant.round({ smallestUnit: 'second' });
+assert.sameValue(implicit.epochNanoseconds, 1_000_000_001_000_000_000n, "default roundingIncrement is 1");
diff --git a/test/built-ins/Temporal/Instant/prototype/round/roundingincrement-wrong-type.js b/test/built-ins/Temporal/Instant/prototype/round/roundingincrement-wrong-type.js
new file mode 100644
index 00000000000..b7c862ff006
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/round/roundingincrement-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.round
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.instant.prototype.round step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => instant.round({ smallestUnit: 'second', roundingIncrement }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_001_000_000_000n, descr),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_000_000_000n, descr),
+);
diff --git a/test/built-ins/Temporal/Instant/prototype/round/roundingmode-invalid-string.js b/test/built-ins/Temporal/Instant/prototype/round/roundingmode-invalid-string.js
new file mode 100644
index 00000000000..7f3ed29f4a2
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/round/roundingmode-invalid-string.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.round
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+assert.throws(RangeError, () => instant.round({ smallestUnit: "microsecond", roundingMode: "other string" }));
diff --git a/test/built-ins/Temporal/Instant/prototype/round/roundingmode-undefined.js b/test/built-ins/Temporal/Instant/prototype/round/roundingmode-undefined.js
new file mode 100644
index 00000000000..7421d6a1fb2
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/round/roundingmode-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.round
+description: Fallback value for roundingMode option
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+
+const explicit1 = instant.round({ smallestUnit: "microsecond", roundingMode: undefined });
+assert.sameValue(explicit1.epochNanoseconds, 1_000_000_000_123_988_000n, "default roundingMode is halfExpand");
+const implicit1 = instant.round({ smallestUnit: "microsecond" });
+assert.sameValue(implicit1.epochNanoseconds, 1_000_000_000_123_988_000n, "default roundingMode is halfExpand");
+
+const explicit2 = instant.round({ smallestUnit: "millisecond", roundingMode: undefined });
+assert.sameValue(explicit2.epochNanoseconds, 1_000_000_000_124_000_000n, "default roundingMode is halfExpand");
+const implicit2 = instant.round({ smallestUnit: "millisecond" });
+assert.sameValue(implicit2.epochNanoseconds, 1_000_000_000_124_000_000n, "default roundingMode is halfExpand");
+
+const explicit3 = instant.round({ smallestUnit: "second", roundingMode: undefined });
+assert.sameValue(explicit3.epochNanoseconds, 1_000_000_000_000_000_000n, "default roundingMode is halfExpand");
+const implicit3 = instant.round({ smallestUnit: "second" });
+assert.sameValue(implicit3.epochNanoseconds, 1_000_000_000_000_000_000n, "default roundingMode is halfExpand");
diff --git a/test/built-ins/Temporal/Instant/prototype/round/roundingmode-wrong-type.js b/test/built-ins/Temporal/Instant/prototype/round/roundingmode-wrong-type.js
new file mode 100644
index 00000000000..e21ad2a8453
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/round/roundingmode-wrong-type.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.round
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "halfExpand",
+ (roundingMode) => instant.round({ smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_123_988_000n, descr),
+);
diff --git a/test/built-ins/Temporal/Instant/prototype/round/smallestunit-invalid-string.js b/test/built-ins/Temporal/Instant/prototype/round/smallestunit-invalid-string.js
new file mode 100644
index 00000000000..298c3ce64d6
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/round/smallestunit-invalid-string.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.round
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+assert.throws(RangeError, () => instant.round({ smallestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/Instant/prototype/round/smallestunit-plurals-accepted.js b/test/built-ins/Temporal/Instant/prototype/round/smallestunit-plurals-accepted.js
new file mode 100644
index 00000000000..67765e46c1f
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/round/smallestunit-plurals-accepted.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.round
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+const validUnits = [
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => instant.round({ smallestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/Instant/prototype/round/smallestunit-wrong-type.js b/test/built-ins/Temporal/Instant/prototype/round/smallestunit-wrong-type.js
new file mode 100644
index 00000000000..b7297b688d9
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/round/smallestunit-wrong-type.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.round
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => instant.round({ smallestUnit }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_123_988_000n, descr),
+);
diff --git a/test/built-ins/Temporal/Instant/prototype/round/subclassing-ignored.js b/test/built-ins/Temporal/Instant/prototype/round/subclassing-ignored.js
new file mode 100644
index 00000000000..ad9297663c7
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/round/subclassing-ignored.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.round
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.Instant,
+ [10n],
+ "round",
+ [{ smallestUnit: 'second', roundingMode: 'ceil' }],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 1_000_000_000n, "epochNanoseconds result");
+ },
+);
diff --git a/test/built-ins/Temporal/Instant/prototype/since/argument-zoneddatetime.js b/test/built-ins/Temporal/Instant/prototype/since/argument-zoneddatetime.js
new file mode 100644
index 00000000000..af598018322
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/argument-zoneddatetime.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: Fast path for converting Temporal.ZonedDateTime to Temporal.Instant
+info: |
+ sec-temporal.instant.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalInstant(_other_).
+ sec-temporal-totemporalinstant step 1.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return ! CreateTemporalInstant(_item_.[[Nanoseconds]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalInstantFastPath((datetime) => {
+ const instant = new Temporal.Instant(1_000_000_000_000_000_000n);
+ const result = instant.since(datetime);
+ assert.sameValue(result.total({ unit: "nanoseconds" }), -987654321, "nanoseconds result");
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/since/builtin.js b/test/built-ins/Temporal/Instant/prototype/since/builtin.js
new file mode 100644
index 00000000000..ee873bee6e6
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: >
+ Tests that Temporal.Instant.prototype.since
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Instant.prototype.since),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.since),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.since),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.since.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Instant/prototype/since/instant-string.js b/test/built-ins/Temporal/Instant/prototype/since/instant-string.js
new file mode 100644
index 00000000000..002fbb6257d
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/instant-string.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: Conversion of ISO date-time strings to Temporal.Instant instances
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.since(str), "bare date-time string is not an instant");
+str = "1970-01-01T00:00[America/Vancouver]";
+assert.throws(RangeError, () => instance.since(str), "date-time + IANA annotation is not an instant");
+
+str = "1970-01-01T00:00Z";
+const result1 = instance.since(str);
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z preserves exact time");
+
+str = "1970-01-01T00:00+01:00";
+const result2 = instance.since(str);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 0, 0, 3600, 0, 0, 0, "date-time + offset preserves exact time with offset");
+
+str = "1970-01-01T00:00Z[America/Vancouver]";
+const result3 = instance.since(str);
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation ignores the IANA annotation");
+
+str = "1970-01-01T00:00+01:00[America/Vancouver]";
+const result4 = instance.since(str);
+TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 0, 0, 3600, 0, 0, 0, "date-time + offset + IANA annotation ignores the IANA annotation");
diff --git a/test/built-ins/Temporal/Instant/prototype/since/largestunit-invalid-string.js b/test/built-ins/Temporal/Instant/prototype/since/largestunit-invalid-string.js
new file mode 100644
index 00000000000..aa579652307
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/largestunit-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+assert.throws(RangeError, () => later.since(earlier, { largestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/Instant/prototype/since/largestunit-plurals-accepted.js b/test/built-ins/Temporal/Instant/prototype/since/largestunit-plurals-accepted.js
new file mode 100644
index 00000000000..ef3cce24c16
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/largestunit-plurals-accepted.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_987_654_321n);
+const later = new Temporal.Instant(1_086_403_661_988_655_322n);
+const validUnits = [
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => later.since(earlier, { largestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/Instant/prototype/since/largestunit-undefined.js b/test/built-ins/Temporal/Instant/prototype/since/largestunit-undefined.js
new file mode 100644
index 00000000000..71dfed57102
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/largestunit-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+
+const explicit = later.since(earlier, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 0, 0, 90061, 987, 654, 321, "default largestUnit is second");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 0, 0, 90061, 987, 654, 321, "default largestUnit is second");
diff --git a/test/built-ins/Temporal/Instant/prototype/since/largestunit-wrong-type.js b/test/built-ins/Temporal/Instant/prototype/since/largestunit-wrong-type.js
new file mode 100644
index 00000000000..1d12bef6ebf
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/largestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "hour",
+ (largestUnit) => later.since(earlier, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, descr),
+);
diff --git a/test/built-ins/Temporal/Instant/prototype/since/length.js b/test/built-ins/Temporal/Instant/prototype/since/length.js
new file mode 100644
index 00000000000..558c134b714
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: Temporal.Instant.prototype.since.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.since, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/since/name.js b/test/built-ins/Temporal/Instant/prototype/since/name.js
new file mode 100644
index 00000000000..64e3efa7cb3
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: Temporal.Instant.prototype.since.name is "since".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.since, "name", {
+ value: "since",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/since/not-a-constructor.js b/test/built-ins/Temporal/Instant/prototype/since/not-a-constructor.js
new file mode 100644
index 00000000000..ead085706c8
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: >
+ Temporal.Instant.prototype.since does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Instant.prototype.since();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.since), false,
+ "isConstructor(Temporal.Instant.prototype.since)");
diff --git a/test/built-ins/Temporal/Instant/prototype/since/options-undefined.js b/test/built-ins/Temporal/Instant/prototype/since/options-undefined.js
new file mode 100644
index 00000000000..d631ec2214e
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/options-undefined.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: Verify that undefined options are handled correctly.
+features: [BigInt, Temporal]
+---*/
+
+const earlier = new Temporal.Instant(957270896_987_654_321n);
+const later = new Temporal.Instant(959949296_987_654_322n);
+
+const explicit = later.since(earlier, undefined);
+assert.sameValue(explicit.years, 0, "default largest unit is seconds");
+assert.sameValue(explicit.months, 0, "default largest unit is seconds");
+assert.sameValue(explicit.weeks, 0, "default largest unit is seconds");
+assert.sameValue(explicit.days, 0, "default largest unit is seconds");
+assert.sameValue(explicit.hours, 0, "default largest unit is seconds");
+assert.sameValue(explicit.minutes, 0, "default largest unit is seconds");
+assert.sameValue(explicit.seconds, 2678400, "default largest unit is seconds");
+assert.sameValue(explicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
+
+const implicit = later.since(earlier);
+assert.sameValue(implicit.years, 0, "default largest unit is seconds");
+assert.sameValue(implicit.months, 0, "default largest unit is seconds");
+assert.sameValue(implicit.weeks, 0, "default largest unit is seconds");
+assert.sameValue(implicit.days, 0, "default largest unit is seconds");
+assert.sameValue(implicit.hours, 0, "default largest unit is seconds");
+assert.sameValue(implicit.minutes, 0, "default largest unit is seconds");
+assert.sameValue(implicit.seconds, 2678400, "default largest unit is seconds");
+assert.sameValue(implicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
diff --git a/test/built-ins/Temporal/Instant/prototype/since/prop-desc.js b/test/built-ins/Temporal/Instant/prototype/since/prop-desc.js
new file mode 100644
index 00000000000..c2d01ec43a4
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: The "since" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.since,
+ "function",
+ "`typeof Instant.prototype.since` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "since", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/since/roundingincrement-nan.js b/test/built-ins/Temporal/Instant/prototype/since/roundingincrement-nan.js
new file mode 100644
index 00000000000..dd38a0fcbdf
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/roundingincrement-nan.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.instant.prototype.since step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_987_654_321n);
+const later = new Temporal.Instant(1_000_090_061_988_655_322n);
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: NaN }));
diff --git a/test/built-ins/Temporal/Instant/prototype/since/roundingincrement-non-integer.js b/test/built-ins/Temporal/Instant/prototype/since/roundingincrement-non-integer.js
new file mode 100644
index 00000000000..4df25adc67e
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/roundingincrement-non-integer.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: Rounding for roundingIncrement option
+info: |
+ sec-temporal-totemporalroundingincrement step 7:
+ 7. Set _increment_ to floor(ℝ(_increment_)).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_000_000_000_000_005n);
+const result = later.since(earlier, { roundingIncrement: 2.5 });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, "roundingIncrement 2.5 floors to 2");
diff --git a/test/built-ins/Temporal/Instant/prototype/since/roundingincrement-out-of-range.js b/test/built-ins/Temporal/Instant/prototype/since/roundingincrement-out-of-range.js
new file mode 100644
index 00000000000..a82d37683e1
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/roundingincrement-out-of-range.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ sec-temporal-totemporalroundingincrement step 6:
+ 6. If _increment_ < 1 or _increment_ > _maximum_, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_000_000_000_000_005n);
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: Infinity }));
diff --git a/test/built-ins/Temporal/Instant/prototype/since/roundingincrement-undefined.js b/test/built-ins/Temporal/Instant/prototype/since/roundingincrement-undefined.js
new file mode 100644
index 00000000000..7292eb9f649
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/roundingincrement-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.instant.prototype.since step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_987_654_321n);
+const later = new Temporal.Instant(1_000_090_061_988_655_322n);
+
+const explicit = later.since(earlier, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 0, 0, 90061, 1, 1, 1, "default roundingIncrement is 1");
+
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 0, 0, 90061, 1, 1, 1, "default roundingIncrement is 1");
diff --git a/test/built-ins/Temporal/Instant/prototype/since/roundingincrement-wrong-type.js b/test/built-ins/Temporal/Instant/prototype/since/roundingincrement-wrong-type.js
new file mode 100644
index 00000000000..1993f95373c
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/roundingincrement-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.instant.prototype.since step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_987_654_321n);
+const later = new Temporal.Instant(1_000_090_061_988_655_322n);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => later.since(earlier, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 90061, 1, 1, 1, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 90061, 1, 1, 0, descr),
+);
diff --git a/test/built-ins/Temporal/Instant/prototype/since/roundingmode-invalid-string.js b/test/built-ins/Temporal/Instant/prototype/since/roundingmode-invalid-string.js
new file mode 100644
index 00000000000..66f94cf2ca1
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/roundingmode-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_123_987_500n);
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "microsecond", roundingMode: "other string" }));
diff --git a/test/built-ins/Temporal/Instant/prototype/since/roundingmode-undefined.js b/test/built-ins/Temporal/Instant/prototype/since/roundingmode-undefined.js
new file mode 100644
index 00000000000..3489a14844a
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/roundingmode-undefined.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_123_987_500n);
+
+const explicit1 = later.since(earlier, { smallestUnit: "microsecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 0, 0, 0, 90061, 123, 987, 0, "default roundingMode is trunc");
+const implicit1 = later.since(earlier, { smallestUnit: "microsecond" });
+TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 0, 0, 0, 90061, 123, 987, 0, "default roundingMode is trunc");
+
+const explicit2 = later.since(earlier, { smallestUnit: "millisecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 0, 0, 0, 90061, 123, 0, 0, "default roundingMode is trunc");
+const implicit2 = later.since(earlier, { smallestUnit: "millisecond" });
+TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 0, 0, 0, 90061, 123, 0, 0, "default roundingMode is trunc");
+
+const explicit3 = later.since(earlier, { smallestUnit: "second", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit3, 0, 0, 0, 0, 0, 0, 90061, 0, 0, 0, "default roundingMode is trunc");
+const implicit3 = later.since(earlier, { smallestUnit: "second" });
+TemporalHelpers.assertDuration(implicit3, 0, 0, 0, 0, 0, 0, 90061, 0, 0, 0, "default roundingMode is trunc");
diff --git a/test/built-ins/Temporal/Instant/prototype/since/roundingmode-wrong-type.js b/test/built-ins/Temporal/Instant/prototype/since/roundingmode-wrong-type.js
new file mode 100644
index 00000000000..45a673ad925
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/roundingmode-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_123_987_500n);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => later.since(earlier, { smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 90061, 123, 987, 0, descr),
+);
diff --git a/test/built-ins/Temporal/Instant/prototype/since/smallestunit-invalid-string.js b/test/built-ins/Temporal/Instant/prototype/since/smallestunit-invalid-string.js
new file mode 100644
index 00000000000..64b00b99bc1
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/smallestunit-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/Instant/prototype/since/smallestunit-plurals-accepted.js b/test/built-ins/Temporal/Instant/prototype/since/smallestunit-plurals-accepted.js
new file mode 100644
index 00000000000..bd38110299a
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/smallestunit-plurals-accepted.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_987_654_321n);
+const later = new Temporal.Instant(1_086_403_661_988_655_322n);
+const validUnits = [
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => later.since(earlier, { smallestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/Instant/prototype/since/smallestunit-undefined.js b/test/built-ins/Temporal/Instant/prototype/since/smallestunit-undefined.js
new file mode 100644
index 00000000000..2193dbd54ab
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/smallestunit-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+
+const explicit = later.since(earlier, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 0, 0, 90061, 987, 654, 321, "default smallestUnit is nanosecond");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 0, 0, 90061, 987, 654, 321, "default smallestUnit is nanosecond");
diff --git a/test/built-ins/Temporal/Instant/prototype/since/smallestunit-wrong-type.js b/test/built-ins/Temporal/Instant/prototype/since/smallestunit-wrong-type.js
new file mode 100644
index 00000000000..685d67dc345
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/since/smallestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.since
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => later.since(earlier, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 90061, 987, 654, 0, descr),
+);
diff --git a/test/built-ins/Temporal/Instant/prototype/subtract/argument-string-negative-fractional-units.js b/test/built-ins/Temporal/Instant/prototype/subtract/argument-string-negative-fractional-units.js
new file mode 100644
index 00000000000..4ae54767a6f
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/subtract/argument-string-negative-fractional-units.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.subtract
+description: Strings with fractional duration units are treated with the correct sign
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
+
+const resultHours = instance.subtract("-PT24.567890123H");
+assert.sameValue(resultHours.epochNanoseconds, 1_000_088_444_404_442_799n, "negative fractional hours");
+
+const resultMinutes = instance.subtract("-PT1440.567890123M");
+assert.sameValue(resultMinutes.epochNanoseconds, 1_000_086_434_073_407_379n, "negative fractional minutes");
diff --git a/test/built-ins/Temporal/Instant/prototype/subtract/argument-string.js b/test/built-ins/Temporal/Instant/prototype/subtract/argument-string.js
new file mode 100644
index 00000000000..92fcec8b55e
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/subtract/argument-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.subtract
+description: A string is parsed into the correct object when passed as the argument
+features: [Temporal]
+---*/
+
+const instance = Temporal.Instant.fromEpochSeconds(10);
+const result = instance.subtract("PT3H");
+assert.sameValue(result.epochNanoseconds, -10_790_000_000_000n, "epochNanoseconds result");
diff --git a/test/built-ins/Temporal/Instant/prototype/subtract/builtin.js b/test/built-ins/Temporal/Instant/prototype/subtract/builtin.js
new file mode 100644
index 00000000000..6943c7d8f5c
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/subtract/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.subtract
+description: >
+ Tests that Temporal.Instant.prototype.subtract
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Instant.prototype.subtract),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.subtract),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.subtract),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.subtract.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Instant/prototype/subtract/infinity-throws-rangeerror.js b/test/built-ins/Temporal/Instant/prototype/subtract/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..17fa7b25df0
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/subtract/infinity-throws-rangeerror.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.Instant.prototype.subtract throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.instant.prototype.subtract
+features: [Temporal]
+---*/
+
+const fields = ["hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.Instant.fromEpochSeconds(10);
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: Infinity }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/subtract/length.js b/test/built-ins/Temporal/Instant/prototype/subtract/length.js
new file mode 100644
index 00000000000..dc4716b66e2
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/subtract/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.subtract
+description: Temporal.Instant.prototype.subtract.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.subtract, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/subtract/name.js b/test/built-ins/Temporal/Instant/prototype/subtract/name.js
new file mode 100644
index 00000000000..7803eda270f
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/subtract/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.subtract
+description: Temporal.Instant.prototype.subtract.name is "subtract".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.subtract, "name", {
+ value: "subtract",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/subtract/negative-infinity-throws-rangeerror.js b/test/built-ins/Temporal/Instant/prototype/subtract/negative-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..2aca9588ff8
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/subtract/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.Instant.prototype.subtract throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.instant.prototype.subtract
+features: [Temporal]
+---*/
+
+const fields = ["hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.Instant.fromEpochSeconds(10);
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: -Infinity }));
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/subtract/non-integer-throws-rangeerror.js b/test/built-ins/Temporal/Instant/prototype/subtract/non-integer-throws-rangeerror.js
new file mode 100644
index 00000000000..544377aad09
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/subtract/non-integer-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.subtract
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.subtract({ [field]: -1.5 }));
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/subtract/not-a-constructor.js b/test/built-ins/Temporal/Instant/prototype/subtract/not-a-constructor.js
new file mode 100644
index 00000000000..82745c49d9f
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/subtract/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.subtract
+description: >
+ Temporal.Instant.prototype.subtract does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Instant.prototype.subtract();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.subtract), false,
+ "isConstructor(Temporal.Instant.prototype.subtract)");
diff --git a/test/built-ins/Temporal/Instant/prototype/subtract/order-of-operations.js b/test/built-ins/Temporal/Instant/prototype/subtract/order-of-operations.js
new file mode 100644
index 00000000000..4ffe499d702
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/subtract/order-of-operations.js
@@ -0,0 +1,61 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.subtract
+description: Properties on an object passed to subtract() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(10n);
+const expected = [
+ "get days",
+ "get hours",
+ "get hours.valueOf",
+ "call hours.valueOf",
+ "get microseconds",
+ "get microseconds.valueOf",
+ "call microseconds.valueOf",
+ "get milliseconds",
+ "get milliseconds.valueOf",
+ "call milliseconds.valueOf",
+ "get minutes",
+ "get minutes.valueOf",
+ "call minutes.valueOf",
+ "get months",
+ "get nanoseconds",
+ "get nanoseconds.valueOf",
+ "call nanoseconds.valueOf",
+ "get seconds",
+ "get seconds.valueOf",
+ "call seconds.valueOf",
+ "get weeks",
+ "get years",
+];
+const actual = [];
+const fields = {
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = instance.subtract(argument);
+assert.sameValue(result.epochNanoseconds, -3661001000991n, "epochNanoseconds result");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/Instant/prototype/subtract/prop-desc.js b/test/built-ins/Temporal/Instant/prototype/subtract/prop-desc.js
new file mode 100644
index 00000000000..a6057946a92
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/subtract/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.subtract
+description: The "subtract" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.subtract,
+ "function",
+ "`typeof Instant.prototype.subtract` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "subtract", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/subtract/result-out-of-range.js b/test/built-ins/Temporal/Instant/prototype/subtract/result-out-of-range.js
new file mode 100644
index 00000000000..83804b758c4
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/subtract/result-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.add
+description: RangeError thrown if result is outside representable range
+features: [Temporal]
+---*/
+
+const fields = ["hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.Instant.fromEpochNanoseconds(-8640000_000_000_000_000_000n);
+
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: 1 }));
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/subtract/subclassing-ignored.js b/test/built-ins/Temporal/Instant/prototype/subtract/subclassing-ignored.js
new file mode 100644
index 00000000000..8fe33354a3d
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/subtract/subclassing-ignored.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.subtract
+description: Objects of a subclass are never created as return values for subtract()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.Instant,
+ [10n],
+ "subtract",
+ [{ nanoseconds: 5 }],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 5n, "epochNanoseconds result");
+ },
+);
diff --git a/test/built-ins/Temporal/Instant/prototype/toJSON/basic.js b/test/built-ins/Temporal/Instant/prototype/toJSON/basic.js
new file mode 100644
index 00000000000..9e654434dcb
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toJSON/basic.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tojson
+description: Basic tests for toJSON()
+features: [Temporal]
+---*/
+
+const instantBeforeEpoch = Temporal.Instant.from("1963-02-13T10:36:29.123456789+01:00");
+assert.sameValue(instantBeforeEpoch.toJSON(), "1963-02-13T09:36:29.123456789Z");
+
+const instantAfterEpoch = Temporal.Instant.from("1976-11-18T15:23:30.123456789+01:00");
+assert.sameValue(instantAfterEpoch.toJSON(), "1976-11-18T14:23:30.123456789Z");
+assert.sameValue(instantAfterEpoch.toJSON("+01:00"), "1976-11-18T14:23:30.123456789Z",
+ "after epoch with ignored argument");
diff --git a/test/built-ins/Temporal/Instant/prototype/toJSON/builtin.js b/test/built-ins/Temporal/Instant/prototype/toJSON/builtin.js
new file mode 100644
index 00000000000..725f5f43b35
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toJSON/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tojson
+description: >
+ Tests that Temporal.Instant.prototype.toJSON
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Instant.prototype.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Instant/prototype/toJSON/length.js b/test/built-ins/Temporal/Instant/prototype/toJSON/length.js
new file mode 100644
index 00000000000..9fbcb1fe557
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toJSON/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tojson
+description: Temporal.Instant.prototype.toJSON.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/toJSON/name.js b/test/built-ins/Temporal/Instant/prototype/toJSON/name.js
new file mode 100644
index 00000000000..4cd6d5af4d7
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toJSON/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tojson
+description: Temporal.Instant.prototype.toJSON.name is "toJSON".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/toJSON/negative-epochnanoseconds.js b/test/built-ins/Temporal/Instant/prototype/toJSON/negative-epochnanoseconds.js
new file mode 100644
index 00000000000..18d9cd8a470
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toJSON/negative-epochnanoseconds.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tojson
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.Instant(-13849764_999_999_999n);
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const result = datetime.toJSON();
+assert.sameValue(result, "1969-07-24T16:50:35.000000001Z");
diff --git a/test/built-ins/Temporal/Instant/prototype/toJSON/not-a-constructor.js b/test/built-ins/Temporal/Instant/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 00000000000..12fcee1be71
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toJSON/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tojson
+description: >
+ Temporal.Instant.prototype.toJSON does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Instant.prototype.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.toJSON), false,
+ "isConstructor(Temporal.Instant.prototype.toJSON)");
diff --git a/test/built-ins/Temporal/Instant/prototype/toJSON/prop-desc.js b/test/built-ins/Temporal/Instant/prototype/toJSON/prop-desc.js
new file mode 100644
index 00000000000..05b9d082d73
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toJSON/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tojson
+description: The "toJSON" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.toJSON,
+ "function",
+ "`typeof Instant.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/toLocaleString/builtin.js b/test/built-ins/Temporal/Instant/prototype/toLocaleString/builtin.js
new file mode 100644
index 00000000000..7f6f392a1a8
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toLocaleString/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tolocalestring
+description: >
+ Tests that Temporal.Instant.prototype.toLocaleString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Instant.prototype.toLocaleString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.toLocaleString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.toLocaleString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.toLocaleString.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Instant/prototype/toLocaleString/length.js b/test/built-ins/Temporal/Instant/prototype/toLocaleString/length.js
new file mode 100644
index 00000000000..9e8b438c00d
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toLocaleString/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tolocalestring
+description: Temporal.Instant.prototype.toLocaleString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.toLocaleString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/toLocaleString/locales-undefined.js b/test/built-ins/Temporal/Instant/prototype/toLocaleString/locales-undefined.js
new file mode 100644
index 00000000000..c00ac294a47
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toLocaleString/locales-undefined.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tolocalestring
+description: Omitting the locales argument defaults to the DateTimeFormat default
+features: [BigInt, Temporal]
+---*/
+
+const instant = new Temporal.Instant(957270896_987_650_000n);
+const defaultFormatter = new Intl.DateTimeFormat([], Object.create(null));
+const expected = defaultFormatter.format(instant);
+
+const actualExplicit = instant.toLocaleString(undefined);
+assert.sameValue(actualExplicit, expected, "default locale is determined by Intl.DateTimeFormat");
+
+const actualImplicit = instant.toLocaleString();
+assert.sameValue(actualImplicit, expected, "default locale is determined by Intl.DateTimeFormat");
diff --git a/test/built-ins/Temporal/Instant/prototype/toLocaleString/name.js b/test/built-ins/Temporal/Instant/prototype/toLocaleString/name.js
new file mode 100644
index 00000000000..af3b8f82c0c
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toLocaleString/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tolocalestring
+description: Temporal.Instant.prototype.toLocaleString.name is "toLocaleString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.toLocaleString, "name", {
+ value: "toLocaleString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/toLocaleString/not-a-constructor.js b/test/built-ins/Temporal/Instant/prototype/toLocaleString/not-a-constructor.js
new file mode 100644
index 00000000000..a61747563d8
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toLocaleString/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tolocalestring
+description: >
+ Temporal.Instant.prototype.toLocaleString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Instant.prototype.toLocaleString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.toLocaleString), false,
+ "isConstructor(Temporal.Instant.prototype.toLocaleString)");
diff --git a/test/built-ins/Temporal/Instant/prototype/toLocaleString/options-conflict.js b/test/built-ins/Temporal/Instant/prototype/toLocaleString/options-conflict.js
new file mode 100644
index 00000000000..490d6c27df2
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toLocaleString/options-conflict.js
@@ -0,0 +1,47 @@
+// Copyright (C) 2021 Kate Miháliková. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sup-temporal.instant.prototype.tolocalestring
+description: >
+ Conflicting properties of dateStyle must be rejected with a TypeError for the options argument
+info: |
+ Using sec-temporal-getdatetimeformatpattern:
+ GetDateTimeFormatPattern ( dateStyle, timeStyle, matcher, opt, dataLocaleData, hc )
+
+ 1. If dateStyle is not undefined or timeStyle is not undefined, then
+ a. For each row in Table 7, except the header row, do
+ i. Let prop be the name given in the Property column of the row.
+ ii. Let p be opt.[[]].
+ iii. If p is not undefined, then
+ 1. Throw a TypeError exception.
+features: [BigInt, Temporal]
+---*/
+
+// Table 14 - Supported fields + example value for each field
+const conflictingOptions = [
+ [ "weekday", "short" ],
+ [ "era", "short" ],
+ [ "year", "numeric" ],
+ [ "month", "numeric" ],
+ [ "day", "numeric" ],
+ [ "hour", "numeric" ],
+ [ "minute", "numeric" ],
+ [ "second", "numeric" ],
+ [ "dayPeriod", "short" ],
+ [ "fractionalSecondDigits", 3 ],
+];
+const instant = new Temporal.Instant(957270896_987_650_000n);
+
+assert.sameValue(typeof instant.toLocaleString("en", { dateStyle: "short" }), "string");
+assert.sameValue(typeof instant.toLocaleString("en", { timeStyle: "short" }), "string");
+
+for (const [ option, value ] of conflictingOptions) {
+ assert.throws(TypeError, function() {
+ instant.toLocaleString("en", { [option]: value, dateStyle: "short" });
+ }, `instant.toLocaleString("en", { ${option}: "${value}", dateStyle: "short" }) throws TypeError`);
+
+ assert.throws(TypeError, function() {
+ instant.toLocaleString("en", { [option]: value, timeStyle: "short" });
+ }, `instant.toLocaleString("en", { ${option}: "${value}", timeStyle: "short" }) throws TypeError`);
+}
diff --git a/test/built-ins/Temporal/Instant/prototype/toLocaleString/options-undefined.js b/test/built-ins/Temporal/Instant/prototype/toLocaleString/options-undefined.js
new file mode 100644
index 00000000000..85a9adb2c4f
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toLocaleString/options-undefined.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tolocalestring
+description: Verify that undefined options are handled correctly.
+features: [BigInt, Temporal]
+---*/
+
+const instant = new Temporal.Instant(957270896_987_650_000n);
+const defaultFormatter = new Intl.DateTimeFormat('en', Object.create(null));
+const expected = defaultFormatter.format(instant);
+
+const actualExplicit = instant.toLocaleString('en', undefined);
+assert.sameValue(actualExplicit, expected, "default locale is determined by Intl.DateTimeFormat");
+
+const actualImplicit = instant.toLocaleString('en');
+assert.sameValue(actualImplicit, expected, "default locale is determined by Intl.DateTimeFormat");
diff --git a/test/built-ins/Temporal/Instant/prototype/toLocaleString/prop-desc.js b/test/built-ins/Temporal/Instant/prototype/toLocaleString/prop-desc.js
new file mode 100644
index 00000000000..1b2f50916d2
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toLocaleString/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tolocalestring
+description: The "toLocaleString" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.toLocaleString,
+ "function",
+ "`typeof Instant.prototype.toLocaleString` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "toLocaleString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/basic.js b/test/built-ins/Temporal/Instant/prototype/toString/basic.js
new file mode 100644
index 00000000000..8b4ca47a847
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/basic.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: Basic tests for toString().
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = new Temporal.Instant(217175010_123_456_789n);
+assert.sameValue(afterEpoch.toString(), "1976-11-18T14:23:30.123456789Z", "basic toString() after epoch");
+
+const beforeEpoch = new Temporal.Instant(-217175010_876_543_211n);
+assert.sameValue(beforeEpoch.toString(), "1963-02-13T09:36:29.123456789Z", "basic toString() before epoch");
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/builtin.js b/test/built-ins/Temporal/Instant/prototype/toString/builtin.js
new file mode 100644
index 00000000000..8be3de6aa3a
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: >
+ Tests that Temporal.Instant.prototype.toString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Instant.prototype.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-invalid-string.js b/test/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-invalid-string.js
new file mode 100644
index 00000000000..f75db5a7ffc
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-invalid-string.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option not one of the allowed string values
+info: |
+ sec-getstringornumberoption step 4:
+ 4. If _stringValues_ is not *undefined* and _stringValues_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.instant.prototype.tostring step 6:
+ 6. Let _precision_ be ? ToDurationSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_987_650_000n);
+
+assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: "other string" }));
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-nan.js b/test/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-nan.js
new file mode 100644
index 00000000000..a374669e1e8
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-nan.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.instant.prototype.tostring step 6:
+ 6. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_987_650_000n);
+assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: NaN }));
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-non-integer.js b/test/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-non-integer.js
new file mode 100644
index 00000000000..beb567b6889
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-non-integer.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: Rounding for fractionalSecondDigits option
+info: |
+ sec-getstringornumberoption step 3.b:
+ b. Return floor(ℝ(_value_)).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.instant.prototype.tostring step 6:
+ 6. Let _precision_ be ? ToDurationSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_987_650_000n);
+
+const string = instant.toString({ fractionalSecondDigits: 2.5 });
+assert.sameValue(string, "2001-09-09T01:46:40.98Z", "fractionalSecondDigits 2.5 floors to 2");
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-out-of-range.js b/test/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-out-of-range.js
new file mode 100644
index 00000000000..4506d3ef215
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-out-of-range.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option out of range
+info: |
+ sec-getstringornumberoption step 3.a:
+ a. If _value_ < _minimum_ or _value_ > _maximum_, throw a *RangeError* exception.
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.instant.prototype.tostring step 6:
+ 6. Let _precision_ be ? ToDurationSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_987_650_000n);
+
+assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: -1 }));
+assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: 10 }));
+assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: -Infinity }));
+assert.throws(RangeError, () => instant.toString({ fractionalSecondDigits: Infinity }));
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-undefined.js b/test/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-undefined.js
new file mode 100644
index 00000000000..7bed467685f
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: Fallback value for fractionalSecondDigits option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, *"stringOrNumber"*, *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.instant.prototype.tostring step 6:
+ 6. Let _precision_ be ? ToDurationSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_987_650_000n);
+
+const explicit = instant.toString({ fractionalSecondDigits: undefined });
+assert.sameValue(explicit, "2001-09-09T01:46:40.98765Z", "default fractionalSecondDigits is auto");
+
+const implicit = instant.toString({});
+assert.sameValue(implicit, "2001-09-09T01:46:40.98765Z", "default fractionalSecondDigits is auto");
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-wrong-type.js b/test/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-wrong-type.js
new file mode 100644
index 00000000000..26eb23a3037
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/fractionalseconddigits-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: Type conversions for fractionalSecondDigits option
+info: |
+ sec-getoption steps 8–9:
+ 8. Else if _type_ is Number, then
+ a. Set _value_ to ? ToNumber(value).
+ b. ...
+ 9. Else,
+ a. Set _value_ to ? ToString(value).
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.instant.prototype.tostring step 6:
+ 6. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_987_650_000n);
+TemporalHelpers.checkFractionalSecondDigitsOptionWrongType(instant);
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/length.js b/test/built-ins/Temporal/Instant/prototype/toString/length.js
new file mode 100644
index 00000000000..9e3d79fd030
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: Temporal.Instant.prototype.toString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/name.js b/test/built-ins/Temporal/Instant/prototype/toString/name.js
new file mode 100644
index 00000000000..987192428db
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: Temporal.Instant.prototype.toString.name is "toString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/negative-epochnanoseconds.js b/test/built-ins/Temporal/Instant/prototype/toString/negative-epochnanoseconds.js
new file mode 100644
index 00000000000..7839fd63360
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/negative-epochnanoseconds.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.Instant(-13849764_999_999_999n);
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const result = datetime.toString();
+assert.sameValue(result, "1969-07-24T16:50:35.000000001Z");
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/not-a-constructor.js b/test/built-ins/Temporal/Instant/prototype/toString/not-a-constructor.js
new file mode 100644
index 00000000000..424b2abdc1c
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: >
+ Temporal.Instant.prototype.toString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Instant.prototype.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.toString), false,
+ "isConstructor(Temporal.Instant.prototype.toString)");
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/options-undefined.js b/test/built-ins/Temporal/Instant/prototype/toString/options-undefined.js
new file mode 100644
index 00000000000..08dcbab63c6
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/options-undefined.js
@@ -0,0 +1,38 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+includes: [compareArray.js]
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [];
+
+const instant = Temporal.Instant.from("1975-02-02T14:25:36.12345Z");
+
+Object.defineProperty(Temporal.TimeZone, "from", {
+ get() {
+ actual.push("get Temporal.TimeZone.from");
+ return function(identifier) {
+ actual.push("call Temporal.TimeZone.from");
+ assert.sameValue(identifier, "UTC");
+ };
+ },
+});
+
+assert.sameValue(
+ instant.toString(),
+ "1975-02-02T14:25:36.12345Z",
+ "default time zone is none, precision is auto, and rounding is trunc"
+);
+assert.compareArray(actual, expected);
+
+assert.sameValue(
+ instant.toString(undefined),
+ "1975-02-02T14:25:36.12345Z",
+ "default time zone is none, precision is auto, and rounding is trunc"
+);
+assert.compareArray(actual, expected);
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/precision.js b/test/built-ins/Temporal/Instant/prototype/toString/precision.js
new file mode 100644
index 00000000000..469d806b4a3
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/precision.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: toString() produces a fractional part of the correct length
+features: [Temporal]
+---*/
+
+const { Instant } = Temporal;
+
+const isoString = '2020-01-01T23:58:57.012034Z';
+const instant = Instant.from(isoString);
+const instantIsoStrMicros = instant.toString({
+ smallestUnit: 'microseconds'
+});
+
+assert.sameValue(instantIsoStrMicros, isoString);
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/prop-desc.js b/test/built-ins/Temporal/Instant/prototype/toString/prop-desc.js
new file mode 100644
index 00000000000..9e175913a61
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: The "toString" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.toString,
+ "function",
+ "`typeof Instant.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/roundingmode-invalid-string.js b/test/built-ins/Temporal/Instant/prototype/toString/roundingmode-invalid-string.js
new file mode 100644
index 00000000000..4a4099b108f
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/roundingmode-invalid-string.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+assert.throws(RangeError, () => instant.toString({ smallestUnit: "microsecond", roundingMode: "other string" }));
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/roundingmode-undefined.js b/test/built-ins/Temporal/Instant/prototype/toString/roundingmode-undefined.js
new file mode 100644
index 00000000000..43a23b933ef
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/roundingmode-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: Fallback value for roundingMode option
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+
+const explicit1 = instant.toString({ smallestUnit: "microsecond", roundingMode: undefined });
+assert.sameValue(explicit1, "2001-09-09T01:46:40.123987Z", "default roundingMode is trunc");
+const implicit1 = instant.toString({ smallestUnit: "microsecond" });
+assert.sameValue(implicit1, "2001-09-09T01:46:40.123987Z", "default roundingMode is trunc");
+
+const explicit2 = instant.toString({ smallestUnit: "millisecond", roundingMode: undefined });
+assert.sameValue(explicit2, "2001-09-09T01:46:40.123Z", "default roundingMode is trunc");
+const implicit2 = instant.toString({ smallestUnit: "millisecond" });
+assert.sameValue(implicit2, "2001-09-09T01:46:40.123Z", "default roundingMode is trunc");
+
+const explicit3 = instant.toString({ smallestUnit: "second", roundingMode: undefined });
+assert.sameValue(explicit3, "2001-09-09T01:46:40Z", "default roundingMode is trunc");
+const implicit3 = instant.toString({ smallestUnit: "second" });
+assert.sameValue(implicit3, "2001-09-09T01:46:40Z", "default roundingMode is trunc");
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/roundingmode-wrong-type.js b/test/built-ins/Temporal/Instant/prototype/toString/roundingmode-wrong-type.js
new file mode 100644
index 00000000000..661b63a7433
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/roundingmode-wrong-type.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => instant.toString({ smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => assert.sameValue(result, "2001-09-09T01:46:40.123987Z", descr),
+);
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/smallestunit-invalid-string.js b/test/built-ins/Temporal/Instant/prototype/toString/smallestunit-invalid-string.js
new file mode 100644
index 00000000000..f6ba6e30cc6
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/smallestunit-invalid-string.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+assert.throws(RangeError, () => instant.toString({ smallestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/smallestunit-plurals-accepted.js b/test/built-ins/Temporal/Instant/prototype/toString/smallestunit-plurals-accepted.js
new file mode 100644
index 00000000000..314bbb55b07
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/smallestunit-plurals-accepted.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_456_789n);
+const validUnits = [
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => instant.toString({ smallestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/smallestunit-undefined.js b/test/built-ins/Temporal/Instant/prototype/toString/smallestunit-undefined.js
new file mode 100644
index 00000000000..0a448b8dc3a
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/smallestunit-undefined.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: Fallback value for smallestUnit option
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+
+const explicit1 = instant.toString({ smallestUnit: undefined, fractionalSecondDigits: 6 });
+assert.sameValue(explicit1, "2001-09-09T01:46:40.123987Z", "default smallestUnit defers to fractionalSecondDigits");
+const implicit1 = instant.toString({ fractionalSecondDigits: 6 });
+assert.sameValue(implicit1, "2001-09-09T01:46:40.123987Z", "default smallestUnit defers to fractionalSecondDigits");
+
+const explicit2 = instant.toString({ smallestUnit: undefined, fractionalSecondDigits: 3 });
+assert.sameValue(explicit2, "2001-09-09T01:46:40.123Z", "default smallestUnit defers to fractionalSecondDigits");
+const implicit2 = instant.toString({ fractionalSecondDigits: 3 });
+assert.sameValue(implicit2, "2001-09-09T01:46:40.123Z", "default smallestUnit defers to fractionalSecondDigits");
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/smallestunit-valid-units.js b/test/built-ins/Temporal/Instant/prototype/toString/smallestunit-valid-units.js
new file mode 100644
index 00000000000..191eec97ed4
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/smallestunit-valid-units.js
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: Valid units for the smallestUnit option
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_456_789n);
+
+assert.sameValue(instant.toString({ smallestUnit: "minute" }), "2001-09-09T01:46Z");
+assert.sameValue(instant.toString({ smallestUnit: "second" }), "2001-09-09T01:46:40Z");
+assert.sameValue(instant.toString({ smallestUnit: "millisecond" }), "2001-09-09T01:46:40.123Z");
+assert.sameValue(instant.toString({ smallestUnit: "microsecond" }), "2001-09-09T01:46:40.123456Z");
+assert.sameValue(instant.toString({ smallestUnit: "nanosecond" }), "2001-09-09T01:46:40.123456789Z");
+
+const notValid = [
+ "era",
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+];
+
+notValid.forEach((smallestUnit) => {
+ assert.throws(RangeError, () => instant.toString({ smallestUnit }), smallestUnit);
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/smallestunit-wrong-type.js b/test/built-ins/Temporal/Instant/prototype/toString/smallestunit-wrong-type.js
new file mode 100644
index 00000000000..2c82e66cbd7
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/smallestunit-wrong-type.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instant = new Temporal.Instant(1_000_000_000_123_987_500n);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => instant.toString({ smallestUnit }),
+ (result, descr) => assert.sameValue(result, "2001-09-09T01:46:40.123987Z", descr),
+);
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..fbed542c45b
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ assert.throws(RangeError, () => instant.toString({ timeZone }));
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..66977904194
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ assert.throws(RangeError, () => instant.toString({ timeZone }));
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..1c76fea6e2b
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ assert.throws(TypeError, () => instant.toString({ timeZone }));
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/timezone-string-datetime.js b/test/built-ins/Temporal/Instant/prototype/toString/timezone-string-datetime.js
new file mode 100644
index 00000000000..7097708233a
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/timezone-string-datetime.js
@@ -0,0 +1,45 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+includes: [arrayContains.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.toString({ timeZone }), "bare date-time string is not a time zone");
+assert.throws(RangeError, () => instance.toString({ timeZone: { timeZone } }), "bare date-time string is not a time zone");
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = instance.toString({ timeZone });
+arrayContains(result1, "UTC", "date-time + Z is UTC time zone");
+const result2 = instance.toString({ timeZone: { timeZone } });
+arrayContains(result2, "UTC", "date-time + Z is UTC time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result3 = instance.toString({ timeZone });
+arrayContains(result3, "-07:00", "date-time + offset is the offset time zone");
+const result4 = instance.toString({ timeZone: { timeZone } });
+arrayContains(result4, "-07:00", "date-time + offset is the offset time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30[America/Vancouver]";
+const result5 = instance.toString({ timeZone });
+arrayContains(result5, "America/Vancouver", "date-time + IANA annotation is the IANA time zone");
+const result6 = instance.toString({ timeZone: { timeZone } });
+arrayContains(result6, "America/Vancouver", "date-time + IANA annotation is the IANA time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30Z[America/Vancouver]";
+const result7 = instance.toString({ timeZone });
+arrayContains(result7, "America/Vancouver", "date-time + Z + IANA annotation is the IANA time zone");
+const result8 = instance.toString({ timeZone: { timeZone } });
+arrayContains(result8, "America/Vancouver", "date-time + Z + IANA annotation is the IANA time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30-07:00[America/Vancouver]";
+const result9 = instance.toString({ timeZone });
+arrayContains(result9, "America/Vancouver", "date-time + offset + IANA annotation is the IANA time zone");
+const result10 = instance.toString({ timeZone: { timeZone } });
+arrayContains(result10, "America/Vancouver", "date-time + offset + IANA annotation is the IANA time zone (string in property bag)");
diff --git a/test/built-ins/Temporal/Instant/prototype/toString/timezone.js b/test/built-ins/Temporal/Instant/prototype/toString/timezone.js
new file mode 100644
index 00000000000..0be6957a229
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toString/timezone.js
@@ -0,0 +1,53 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tostring
+description: Passing a TimeZone to options calls getOffsetNanosecondsFor twice, but not toString
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "has timeZone.timeZone",
+ "get timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+ "get timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+];
+
+const instant = Temporal.Instant.from("1975-02-02T14:25:36.123456Z");
+const timeZone = new Proxy({
+ name: "Custom/TimeZone",
+
+ toString() {
+ actual.push("call timeZone.toString");
+ return TemporalHelpers.toPrimitiveObserver(actual, "Custom/TimeZone", "name");
+ },
+
+ getOffsetNanosecondsFor(instantArg) {
+ actual.push("call timeZone.getOffsetNanosecondsFor");
+ assert.sameValue(instantArg.epochNanoseconds, instant.epochNanoseconds);
+ return -8735135801679;
+ },
+}, {
+ has(target, property) {
+ actual.push(`has timeZone.${property}`);
+ return property in target;
+ },
+ get(target, property) {
+ actual.push(`get timeZone.${property}`);
+ return target[property];
+ },
+});
+
+Object.defineProperty(Temporal.TimeZone, "from", {
+ get() {
+ actual.push("get Temporal.TimeZone.from");
+ return undefined;
+ },
+});
+
+assert.sameValue(instant.toString({ timeZone }), "1975-02-02T12:00:00.987654321-02:25:35.135801679");
+assert.compareArray(actual, expected);
diff --git a/test/built-ins/Temporal/Instant/prototype/toStringTag/prop-desc.js b/test/built-ins/Temporal/Instant/prototype/toStringTag/prop-desc.js
new file mode 100644
index 00000000000..90e55d4b648
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toStringTag/prop-desc.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype-@@tostringtag
+description: The @@toStringTag property of Temporal.Instant
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+const { Instant } = Temporal;
+verifyProperty(Instant.prototype, Symbol.toStringTag, {
+ value: "Temporal.Instant",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/builtin.js b/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/builtin.js
new file mode 100644
index 00000000000..dad9a45457a
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tozoneddatetime
+description: >
+ Tests that Temporal.Instant.prototype.toZonedDateTime
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Instant.prototype.toZonedDateTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.toZonedDateTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.toZonedDateTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.toZonedDateTime.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-temporal-object.js b/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-temporal-object.js
new file mode 100644
index 00000000000..f747c690a95
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-temporal-object.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tozoneddatetime
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.instant.prototype.tozoneddatetime step 6:
+ 6. Let _calendar_ be ? ToTemporalCalendar(_calendarLike_).
+ 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 instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ const result = instant.toZonedDateTime({ timeZone: "UTC", calendar: temporalObject });
+ assert.sameValue(result.calendar, calendar, "Temporal object coerced to calendar");
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/length.js b/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/length.js
new file mode 100644
index 00000000000..58e8a326b4f
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tozoneddatetime
+description: Temporal.Instant.prototype.toZonedDateTime.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.toZonedDateTime, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/name.js b/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/name.js
new file mode 100644
index 00000000000..e2611e375cc
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tozoneddatetime
+description: Temporal.Instant.prototype.toZonedDateTime.name is "toZonedDateTime".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.toZonedDateTime, "name", {
+ value: "toZonedDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/not-a-constructor.js b/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/not-a-constructor.js
new file mode 100644
index 00000000000..5c6dcb51937
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tozoneddatetime
+description: >
+ Temporal.Instant.prototype.toZonedDateTime does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Instant.prototype.toZonedDateTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.toZonedDateTime), false,
+ "isConstructor(Temporal.Instant.prototype.toZonedDateTime)");
diff --git a/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/plain-custom-timezone.js b/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/plain-custom-timezone.js
new file mode 100644
index 00000000000..0c2d574e160
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/plain-custom-timezone.js
@@ -0,0 +1,38 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tozoneddatetime
+description: TimeZone.getPlainDateTimeFor is not called
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "has timeZone.timeZone",
+];
+
+const instant = Temporal.Instant.from("1975-02-02T14:25:36.123456789Z");
+const dateTime = Temporal.PlainDateTime.from("1963-07-02T12:00:00.987654321");
+const calendar = Temporal.Calendar.from("iso8601");
+const timeZone = new Proxy({
+ getPlainDateTimeFor() {
+ actual.push("call timeZone.getPlainDateTimeFor");
+ return dateTime;
+ }
+}, {
+ has(target, property) {
+ actual.push(`has timeZone.${property}`);
+ return property in target;
+ },
+ get(target, property) {
+ actual.push(`get timeZone.${property}`);
+ return target[property];
+ },
+});
+
+const result = instant.toZonedDateTime({ timeZone, calendar });
+assert.sameValue(result.epochNanoseconds, instant.epochNanoseconds);
+
+assert.compareArray(actual, expected);
diff --git a/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/prop-desc.js b/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/prop-desc.js
new file mode 100644
index 00000000000..9269d9fbc43
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tozoneddatetime
+description: The "toZonedDateTime" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.toZonedDateTime,
+ "function",
+ "`typeof Instant.prototype.toZonedDateTime` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "toZonedDateTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string-datetime.js b/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string-datetime.js
new file mode 100644
index 00000000000..650765c7edc
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string-datetime.js
@@ -0,0 +1,44 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tozoneddatetime
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.toZonedDateTime({ timeZone, calendar: "iso8601" }), "bare date-time string is not a time zone");
+assert.throws(RangeError, () => instance.toZonedDateTime({ timeZone: { timeZone }, calendar: "iso8601" }), "bare date-time string is not a time zone");
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = instance.toZonedDateTime({ timeZone, calendar: "iso8601" });
+assert.sameValue(result1.timeZone.id, "UTC", "date-time + Z is UTC time zone");
+const result2 = instance.toZonedDateTime({ timeZone: { timeZone }, calendar: "iso8601" });
+assert.sameValue(result2.timeZone.id, "UTC", "date-time + Z is UTC time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result3 = instance.toZonedDateTime({ timeZone, calendar: "iso8601" });
+assert.sameValue(result3.timeZone.id, "-07:00", "date-time + offset is the offset time zone");
+const result4 = instance.toZonedDateTime({ timeZone: { timeZone }, calendar: "iso8601" });
+assert.sameValue(result4.timeZone.id, "-07:00", "date-time + offset is the offset time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30[America/Vancouver]";
+const result5 = instance.toZonedDateTime({ timeZone, calendar: "iso8601" });
+assert.sameValue(result5.timeZone.id, "America/Vancouver", "date-time + IANA annotation is the IANA time zone");
+const result6 = instance.toZonedDateTime({ timeZone: { timeZone }, calendar: "iso8601" });
+assert.sameValue(result6.timeZone.id, "America/Vancouver", "date-time + IANA annotation is the IANA time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30Z[America/Vancouver]";
+const result7 = instance.toZonedDateTime({ timeZone, calendar: "iso8601" });
+assert.sameValue(result7.timeZone.id, "America/Vancouver", "date-time + Z + IANA annotation is the IANA time zone");
+const result8 = instance.toZonedDateTime({ timeZone: { timeZone }, calendar: "iso8601" });
+assert.sameValue(result8.timeZone.id, "America/Vancouver", "date-time + Z + IANA annotation is the IANA time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30-07:00[America/Vancouver]";
+const result9 = instance.toZonedDateTime({ timeZone, calendar: "iso8601" });
+assert.sameValue(result9.timeZone.id, "America/Vancouver", "date-time + offset + IANA annotation is the IANA time zone");
+const result10 = instance.toZonedDateTime({ timeZone: { timeZone }, calendar: "iso8601" });
+assert.sameValue(result10.timeZone.id, "America/Vancouver", "date-time + offset + IANA annotation is the IANA time zone (string in property bag)");
diff --git a/test/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/builtin.js b/test/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/builtin.js
new file mode 100644
index 00000000000..f3b1670bccf
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tozoneddatetimeiso
+description: >
+ Tests that Temporal.Instant.prototype.toZonedDateTimeISO
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Instant.prototype.toZonedDateTimeISO),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.toZonedDateTimeISO),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.toZonedDateTimeISO),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.toZonedDateTimeISO.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/length.js b/test/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/length.js
new file mode 100644
index 00000000000..b3de7d83840
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tozoneddatetimeiso
+description: Temporal.Instant.prototype.toZonedDateTimeISO.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.toZonedDateTimeISO, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/name.js b/test/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/name.js
new file mode 100644
index 00000000000..70918b4bd15
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tozoneddatetimeiso
+description: Temporal.Instant.prototype.toZonedDateTimeISO.name is "toZonedDateTimeISO".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.toZonedDateTimeISO, "name", {
+ value: "toZonedDateTimeISO",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/not-a-constructor.js b/test/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/not-a-constructor.js
new file mode 100644
index 00000000000..72f0327911b
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tozoneddatetimeiso
+description: >
+ Temporal.Instant.prototype.toZonedDateTimeISO does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Instant.prototype.toZonedDateTimeISO();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.toZonedDateTimeISO), false,
+ "isConstructor(Temporal.Instant.prototype.toZonedDateTimeISO)");
diff --git a/test/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/prop-desc.js b/test/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/prop-desc.js
new file mode 100644
index 00000000000..652a9dcc783
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tozoneddatetimeiso
+description: The "toZonedDateTimeISO" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.toZonedDateTimeISO,
+ "function",
+ "`typeof Instant.prototype.toZonedDateTimeISO` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "toZonedDateTimeISO", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string-datetime.js b/test/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string-datetime.js
new file mode 100644
index 00000000000..37b241b728e
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string-datetime.js
@@ -0,0 +1,44 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tozoneddatetimeiso
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.toZonedDateTimeISO(timeZone), "bare date-time string is not a time zone");
+assert.throws(RangeError, () => instance.toZonedDateTimeISO({ timeZone }), "bare date-time string is not a time zone");
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = instance.toZonedDateTimeISO(timeZone);
+assert.sameValue(result1.timeZone.id, "UTC", "date-time + Z is UTC time zone");
+const result2 = instance.toZonedDateTimeISO({ timeZone });
+assert.sameValue(result2.timeZone.id, "UTC", "date-time + Z is UTC time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result3 = instance.toZonedDateTimeISO(timeZone);
+assert.sameValue(result3.timeZone.id, "-07:00", "date-time + offset is the offset time zone");
+const result4 = instance.toZonedDateTimeISO({ timeZone });
+assert.sameValue(result4.timeZone.id, "-07:00", "date-time + offset is the offset time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30[America/Vancouver]";
+const result5 = instance.toZonedDateTimeISO(timeZone);
+assert.sameValue(result5.timeZone.id, "America/Vancouver", "date-time + IANA annotation is the IANA time zone");
+const result6 = instance.toZonedDateTimeISO({ timeZone });
+assert.sameValue(result6.timeZone.id, "America/Vancouver", "date-time + IANA annotation is the IANA time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30Z[America/Vancouver]";
+const result7 = instance.toZonedDateTimeISO(timeZone);
+assert.sameValue(result7.timeZone.id, "America/Vancouver", "date-time + Z + IANA annotation is the IANA time zone");
+const result8 = instance.toZonedDateTimeISO({ timeZone });
+assert.sameValue(result8.timeZone.id, "America/Vancouver", "date-time + Z + IANA annotation is the IANA time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30-07:00[America/Vancouver]";
+const result9 = instance.toZonedDateTimeISO(timeZone);
+assert.sameValue(result9.timeZone.id, "America/Vancouver", "date-time + offset + IANA annotation is the IANA time zone");
+const result10 = instance.toZonedDateTimeISO({ timeZone });
+assert.sameValue(result10.timeZone.id, "America/Vancouver", "date-time + offset + IANA annotation is the IANA time zone (string in property bag)");
diff --git a/test/built-ins/Temporal/Instant/prototype/until/argument-zoneddatetime.js b/test/built-ins/Temporal/Instant/prototype/until/argument-zoneddatetime.js
new file mode 100644
index 00000000000..de0305a810e
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/argument-zoneddatetime.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: Fast path for converting Temporal.ZonedDateTime to Temporal.Instant
+info: |
+ sec-temporal.instant.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalInstant(_other_).
+ sec-temporal-totemporalinstant step 1.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return ! CreateTemporalInstant(_item_.[[Nanoseconds]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalInstantFastPath((datetime) => {
+ const instant = new Temporal.Instant(1_000_000_000_000_000_000n);
+ const result = instant.until(datetime);
+ assert.sameValue(result.total({ unit: "nanoseconds" }), 987654321, "nanoseconds result");
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/until/builtin.js b/test/built-ins/Temporal/Instant/prototype/until/builtin.js
new file mode 100644
index 00000000000..53899eb07d1
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: >
+ Tests that Temporal.Instant.prototype.until
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Instant.prototype.until),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.until),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.until),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.until.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Instant/prototype/until/instant-string.js b/test/built-ins/Temporal/Instant/prototype/until/instant-string.js
new file mode 100644
index 00000000000..c300ad57cce
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/instant-string.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: Conversion of ISO date-time strings to Temporal.Instant instances
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Instant(0n);
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.until(str), "bare date-time string is not an instant");
+str = "1970-01-01T00:00[America/Vancouver]";
+assert.throws(RangeError, () => instance.until(str), "date-time + IANA annotation is not an instant");
+
+str = "1970-01-01T00:00Z";
+const result1 = instance.until(str);
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z preserves exact time");
+
+str = "1970-01-01T00:00+01:00";
+const result2 = instance.until(str);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 0, 0, -3600, 0, 0, 0, "date-time + offset preserves exact time with offset");
+
+str = "1970-01-01T00:00Z[America/Vancouver]";
+const result3 = instance.until(str);
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation ignores the IANA annotation");
+
+str = "1970-01-01T00:00+01:00[America/Vancouver]";
+const result4 = instance.until(str);
+TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 0, 0, -3600, 0, 0, 0, "date-time + offset + IANA annotation ignores the IANA annotation");
diff --git a/test/built-ins/Temporal/Instant/prototype/until/largestunit-invalid-string.js b/test/built-ins/Temporal/Instant/prototype/until/largestunit-invalid-string.js
new file mode 100644
index 00000000000..fc9ab07b5e2
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/largestunit-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+assert.throws(RangeError, () => earlier.until(later, { largestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/Instant/prototype/until/largestunit-plurals-accepted.js b/test/built-ins/Temporal/Instant/prototype/until/largestunit-plurals-accepted.js
new file mode 100644
index 00000000000..ac3e5ba3254
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/largestunit-plurals-accepted.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_987_654_321n);
+const later = new Temporal.Instant(1_086_403_661_988_655_322n);
+const validUnits = [
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => earlier.until(later, { largestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/Instant/prototype/until/largestunit-undefined.js b/test/built-ins/Temporal/Instant/prototype/until/largestunit-undefined.js
new file mode 100644
index 00000000000..b769ac2e814
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/largestunit-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+
+const explicit = earlier.until(later, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 0, 0, 90061, 987, 654, 321, "default largestUnit is second");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 0, 0, 90061, 987, 654, 321, "default largestUnit is second");
diff --git a/test/built-ins/Temporal/Instant/prototype/until/largestunit-wrong-type.js b/test/built-ins/Temporal/Instant/prototype/until/largestunit-wrong-type.js
new file mode 100644
index 00000000000..c5c9c2c0841
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/largestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "hour",
+ (largestUnit) => earlier.until(later, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, descr),
+);
diff --git a/test/built-ins/Temporal/Instant/prototype/until/length.js b/test/built-ins/Temporal/Instant/prototype/until/length.js
new file mode 100644
index 00000000000..a226c7a24fd
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: Temporal.Instant.prototype.until.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.until, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/until/name.js b/test/built-ins/Temporal/Instant/prototype/until/name.js
new file mode 100644
index 00000000000..c2dd28a6633
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: Temporal.Instant.prototype.until.name is "until".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.until, "name", {
+ value: "until",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/until/not-a-constructor.js b/test/built-ins/Temporal/Instant/prototype/until/not-a-constructor.js
new file mode 100644
index 00000000000..1df57185ba0
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: >
+ Temporal.Instant.prototype.until does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Instant.prototype.until();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.until), false,
+ "isConstructor(Temporal.Instant.prototype.until)");
diff --git a/test/built-ins/Temporal/Instant/prototype/until/options-undefined.js b/test/built-ins/Temporal/Instant/prototype/until/options-undefined.js
new file mode 100644
index 00000000000..c5f1469de21
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/options-undefined.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: Verify that undefined options are handled correctly.
+features: [BigInt, Temporal]
+---*/
+
+const earlier = new Temporal.Instant(957270896_987_654_321n);
+const later = new Temporal.Instant(959949296_987_654_322n);
+
+const explicit = earlier.until(later, undefined);
+assert.sameValue(explicit.years, 0, "default largest unit is seconds");
+assert.sameValue(explicit.months, 0, "default largest unit is seconds");
+assert.sameValue(explicit.weeks, 0, "default largest unit is seconds");
+assert.sameValue(explicit.days, 0, "default largest unit is seconds");
+assert.sameValue(explicit.hours, 0, "default largest unit is seconds");
+assert.sameValue(explicit.minutes, 0, "default largest unit is seconds");
+assert.sameValue(explicit.seconds, 2678400, "default largest unit is seconds");
+assert.sameValue(explicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
+
+const implicit = earlier.until(later);
+assert.sameValue(implicit.years, 0, "default largest unit is seconds");
+assert.sameValue(implicit.months, 0, "default largest unit is seconds");
+assert.sameValue(implicit.weeks, 0, "default largest unit is seconds");
+assert.sameValue(implicit.days, 0, "default largest unit is seconds");
+assert.sameValue(implicit.hours, 0, "default largest unit is seconds");
+assert.sameValue(implicit.minutes, 0, "default largest unit is seconds");
+assert.sameValue(implicit.seconds, 2678400, "default largest unit is seconds");
+assert.sameValue(implicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
diff --git a/test/built-ins/Temporal/Instant/prototype/until/prop-desc.js b/test/built-ins/Temporal/Instant/prototype/until/prop-desc.js
new file mode 100644
index 00000000000..7bde5ec90f5
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: The "until" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.until,
+ "function",
+ "`typeof Instant.prototype.until` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "until", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/until/roundingincrement-nan.js b/test/built-ins/Temporal/Instant/prototype/until/roundingincrement-nan.js
new file mode 100644
index 00000000000..f4dd4f4ccc1
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/roundingincrement-nan.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.instant.prototype.until step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_987_654_321n);
+const later = new Temporal.Instant(1_000_090_061_988_655_322n);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: NaN }));
diff --git a/test/built-ins/Temporal/Instant/prototype/until/roundingincrement-non-integer.js b/test/built-ins/Temporal/Instant/prototype/until/roundingincrement-non-integer.js
new file mode 100644
index 00000000000..0f5e2acbf8a
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/roundingincrement-non-integer.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: Rounding for roundingIncrement option
+info: |
+ sec-temporal-totemporalroundingincrement step 7:
+ 7. Set _increment_ to floor(ℝ(_increment_)).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_000_000_000_000_005n);
+const result = earlier.until(later, { roundingIncrement: 2.5 });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, "roundingIncrement 2.5 floors to 2");
diff --git a/test/built-ins/Temporal/Instant/prototype/until/roundingincrement-out-of-range.js b/test/built-ins/Temporal/Instant/prototype/until/roundingincrement-out-of-range.js
new file mode 100644
index 00000000000..738a8070e33
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/roundingincrement-out-of-range.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ sec-temporal-totemporalroundingincrement step 6:
+ 6. If _increment_ < 1 or _increment_ > _maximum_, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_000_000_000_000_005n);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: Infinity }));
diff --git a/test/built-ins/Temporal/Instant/prototype/until/roundingincrement-undefined.js b/test/built-ins/Temporal/Instant/prototype/until/roundingincrement-undefined.js
new file mode 100644
index 00000000000..447dc903028
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/roundingincrement-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.instant.prototype.until step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_987_654_321n);
+const later = new Temporal.Instant(1_000_090_061_988_655_322n);
+
+const explicit = earlier.until(later, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 0, 0, 90061, 1, 1, 1, "default roundingIncrement is 1");
+
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 0, 0, 90061, 1, 1, 1, "default roundingIncrement is 1");
diff --git a/test/built-ins/Temporal/Instant/prototype/until/roundingincrement-wrong-type.js b/test/built-ins/Temporal/Instant/prototype/until/roundingincrement-wrong-type.js
new file mode 100644
index 00000000000..95431bc3203
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/roundingincrement-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.instant.prototype.until step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_987_654_321n);
+const later = new Temporal.Instant(1_000_090_061_988_655_322n);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => earlier.until(later, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 90061, 1, 1, 1, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 90061, 1, 1, 0, descr),
+);
diff --git a/test/built-ins/Temporal/Instant/prototype/until/roundingmode-invalid-string.js b/test/built-ins/Temporal/Instant/prototype/until/roundingmode-invalid-string.js
new file mode 100644
index 00000000000..fc8aaed464f
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/roundingmode-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_123_987_500n);
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "microsecond", roundingMode: "other string" }));
diff --git a/test/built-ins/Temporal/Instant/prototype/until/roundingmode-undefined.js b/test/built-ins/Temporal/Instant/prototype/until/roundingmode-undefined.js
new file mode 100644
index 00000000000..f133f8ce608
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/roundingmode-undefined.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_123_987_500n);
+
+const explicit1 = earlier.until(later, { smallestUnit: "microsecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 0, 0, 0, 90061, 123, 987, 0, "default roundingMode is trunc");
+const implicit1 = earlier.until(later, { smallestUnit: "microsecond" });
+TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 0, 0, 0, 90061, 123, 987, 0, "default roundingMode is trunc");
+
+const explicit2 = earlier.until(later, { smallestUnit: "millisecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 0, 0, 0, 90061, 123, 0, 0, "default roundingMode is trunc");
+const implicit2 = earlier.until(later, { smallestUnit: "millisecond" });
+TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 0, 0, 0, 90061, 123, 0, 0, "default roundingMode is trunc");
+
+const explicit3 = earlier.until(later, { smallestUnit: "second", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit3, 0, 0, 0, 0, 0, 0, 90061, 0, 0, 0, "default roundingMode is trunc");
+const implicit3 = earlier.until(later, { smallestUnit: "second" });
+TemporalHelpers.assertDuration(implicit3, 0, 0, 0, 0, 0, 0, 90061, 0, 0, 0, "default roundingMode is trunc");
diff --git a/test/built-ins/Temporal/Instant/prototype/until/roundingmode-wrong-type.js b/test/built-ins/Temporal/Instant/prototype/until/roundingmode-wrong-type.js
new file mode 100644
index 00000000000..6a6620d2c4c
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/roundingmode-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_123_987_500n);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => earlier.until(later, { smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 90061, 123, 987, 0, descr),
+);
diff --git a/test/built-ins/Temporal/Instant/prototype/until/smallestunit-invalid-string.js b/test/built-ins/Temporal/Instant/prototype/until/smallestunit-invalid-string.js
new file mode 100644
index 00000000000..c6901a49112
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/smallestunit-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/Instant/prototype/until/smallestunit-plurals-accepted.js b/test/built-ins/Temporal/Instant/prototype/until/smallestunit-plurals-accepted.js
new file mode 100644
index 00000000000..04fb18fe845
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/smallestunit-plurals-accepted.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_987_654_321n);
+const later = new Temporal.Instant(1_086_403_661_988_655_322n);
+const validUnits = [
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => earlier.until(later, { smallestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/Instant/prototype/until/smallestunit-undefined.js b/test/built-ins/Temporal/Instant/prototype/until/smallestunit-undefined.js
new file mode 100644
index 00000000000..2a8586f10e7
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/smallestunit-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+
+const explicit = earlier.until(later, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 0, 0, 90061, 987, 654, 321, "default smallestUnit is nanosecond");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 0, 0, 90061, 987, 654, 321, "default smallestUnit is nanosecond");
diff --git a/test/built-ins/Temporal/Instant/prototype/until/smallestunit-wrong-type.js b/test/built-ins/Temporal/Instant/prototype/until/smallestunit-wrong-type.js
new file mode 100644
index 00000000000..564392a892e
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/until/smallestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.until
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.Instant(1_000_000_000_000_000_000n);
+const later = new Temporal.Instant(1_000_090_061_987_654_321n);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => earlier.until(later, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 90061, 987, 654, 0, descr),
+);
diff --git a/test/built-ins/Temporal/Instant/prototype/valueOf/builtin.js b/test/built-ins/Temporal/Instant/prototype/valueOf/builtin.js
new file mode 100644
index 00000000000..fb57bf30432
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/valueOf/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.valueof
+description: >
+ Tests that Temporal.Instant.prototype.valueOf
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Instant.prototype.valueOf),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Instant.prototype.valueOf),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Instant.prototype.valueOf),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Instant.prototype.valueOf.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/Instant/prototype/valueOf/length.js b/test/built-ins/Temporal/Instant/prototype/valueOf/length.js
new file mode 100644
index 00000000000..83d5edcf79c
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/valueOf/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.valueof
+description: Temporal.Instant.prototype.valueOf.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.valueOf, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/valueOf/name.js b/test/built-ins/Temporal/Instant/prototype/valueOf/name.js
new file mode 100644
index 00000000000..16b67068d3d
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/valueOf/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.valueof
+description: Temporal.Instant.prototype.valueOf.name is "valueOf".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Instant.prototype.valueOf, "name", {
+ value: "valueOf",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Instant/prototype/valueOf/not-a-constructor.js b/test/built-ins/Temporal/Instant/prototype/valueOf/not-a-constructor.js
new file mode 100644
index 00000000000..a21b57926ae
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/valueOf/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.valueof
+description: >
+ Temporal.Instant.prototype.valueOf does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Instant.prototype.valueOf();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Instant.prototype.valueOf), false,
+ "isConstructor(Temporal.Instant.prototype.valueOf)");
diff --git a/test/built-ins/Temporal/Instant/prototype/valueOf/prop-desc.js b/test/built-ins/Temporal/Instant/prototype/valueOf/prop-desc.js
new file mode 100644
index 00000000000..dd6a1670bf9
--- /dev/null
+++ b/test/built-ins/Temporal/Instant/prototype/valueOf/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.valueof
+description: The "valueOf" property of Temporal.Instant.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Instant.prototype.valueOf,
+ "function",
+ "`typeof Instant.prototype.valueOf` is `function`"
+);
+
+verifyProperty(Temporal.Instant.prototype, "valueOf", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Now/instant/length.js b/test/built-ins/Temporal/Now/instant/length.js
index 3e67b606afe..43071a68690 100644
--- a/test/built-ins/Temporal/Now/instant/length.js
+++ b/test/built-ins/Temporal/Now/instant/length.js
@@ -3,6 +3,15 @@
/*---
esid: sec-temporal.now.instant
description: Temporal.Now.instant.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
includes: [propertyHelper.js]
features: [Temporal]
---*/
diff --git a/test/built-ins/Temporal/Now/plainDate/calendar-temporal-object.js b/test/built-ins/Temporal/Now/plainDate/calendar-temporal-object.js
new file mode 100644
index 00000000000..3de185cbf04
--- /dev/null
+++ b/test/built-ins/Temporal/Now/plainDate/calendar-temporal-object.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.plaindate
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.now.plaindate step 1:
+ 1. Let _dateTime_ be ? SystemDateTime(_temporalTimeZoneLike_, _calendar_).
+ sec-temporal-systemdatetime step 3:
+ 3. Let _calendar_ be ? ToTemporalCalendar(_calendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const result = Temporal.Now.plainDate(temporalObject);
+ assert.sameValue(result.calendar, calendar, "Temporal object coerced to calendar");
+});
diff --git a/test/built-ins/Temporal/Now/plainDate/length.js b/test/built-ins/Temporal/Now/plainDate/length.js
new file mode 100644
index 00000000000..0b9112b7d05
--- /dev/null
+++ b/test/built-ins/Temporal/Now/plainDate/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.plaindate
+description: Temporal.Now.plainDate.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Now.plainDate, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..93144329fa5
--- /dev/null
+++ b/test/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.plaindate
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(RangeError, () => Temporal.Now.plainDate("iso8601", timeZone));
+});
diff --git a/test/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..4361571356d
--- /dev/null
+++ b/test/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.plaindate
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(RangeError, () => Temporal.Now.plainDate("iso8601", timeZone));
+});
diff --git a/test/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..388e9e0e179
--- /dev/null
+++ b/test/built-ins/Temporal/Now/plainDate/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.plaindate
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(TypeError, () => Temporal.Now.plainDate("iso8601", timeZone));
+});
diff --git a/test/built-ins/Temporal/Now/plainDate/timezone-string-datetime.js b/test/built-ins/Temporal/Now/plainDate/timezone-string-datetime.js
new file mode 100644
index 00000000000..440b2c391d4
--- /dev/null
+++ b/test/built-ins/Temporal/Now/plainDate/timezone-string-datetime.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.plaindate
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => Temporal.Now.plainDate("iso8601", timeZone), "bare date-time string is not a time zone");
+assert.throws(RangeError, () => Temporal.Now.plainDate("iso8601", { timeZone }), "bare date-time string is not a time zone");
+
+// The following are all valid strings so should not throw:
+
+[
+ "2021-08-19T17:30Z",
+ "2021-08-19T17:30-07:00",
+ "2021-08-19T17:30[America/Vancouver]",
+ "2021-08-19T17:30Z[America/Vancouver]",
+ "2021-08-19T17:30-07:00[America/Vancouver]",
+].forEach((timeZone) => {
+ Temporal.Now.plainDate("iso8601", timeZone);
+ Temporal.Now.plainDate("iso8601", { timeZone });
+});
diff --git a/test/built-ins/Temporal/Now/plainDate/toPlainDate-override.js b/test/built-ins/Temporal/Now/plainDate/toPlainDate-override.js
new file mode 100644
index 00000000000..d63fd755562
--- /dev/null
+++ b/test/built-ins/Temporal/Now/plainDate/toPlainDate-override.js
@@ -0,0 +1,48 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.plaindate
+description: PlainDateTime.toPlainDate is not observably called
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "has timeZone.timeZone",
+ "get timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+];
+
+Object.defineProperty(Temporal.PlainDateTime.prototype, "toPlainDate", {
+ get() {
+ actual.push("get Temporal.PlainDateTime.prototype.toPlainDate");
+ return function() {
+ actual.push("call Temporal.PlainDateTime.prototype.toPlainDate");
+ };
+ },
+});
+
+const timeZone = new Proxy({
+ getOffsetNanosecondsFor(instant) {
+ actual.push("call timeZone.getOffsetNanosecondsFor");
+ assert.sameValue(instant instanceof Temporal.Instant, true, "Instant");
+ return 86399_999_999_999;
+ },
+}, {
+ has(target, property) {
+ actual.push(`has timeZone.${property}`);
+ return property in target;
+ },
+ get(target, property) {
+ actual.push(`get timeZone.${property}`);
+ return target[property];
+ },
+});
+
+const result = Temporal.Now.plainDate("iso8601", timeZone);
+assert.notSameValue(result, undefined);
+assert.sameValue(result instanceof Temporal.PlainDate, true);
+
+assert.compareArray(actual, expected);
diff --git a/test/built-ins/Temporal/Now/plainDateISO/length.js b/test/built-ins/Temporal/Now/plainDateISO/length.js
new file mode 100644
index 00000000000..6906572e83a
--- /dev/null
+++ b/test/built-ins/Temporal/Now/plainDateISO/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.plaindateiso
+description: Temporal.Now.plainDateISO.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Now.plainDateISO, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..8986a6dcbe9
--- /dev/null
+++ b/test/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.plaindateiso
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(RangeError, () => Temporal.Now.plainDateISO(timeZone));
+});
diff --git a/test/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..c69d1b46ec2
--- /dev/null
+++ b/test/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.plaindateiso
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(RangeError, () => Temporal.Now.plainDateISO(timeZone));
+});
diff --git a/test/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..3f66b8492da
--- /dev/null
+++ b/test/built-ins/Temporal/Now/plainDateISO/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.plaindateiso
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(TypeError, () => Temporal.Now.plainDateISO(timeZone));
+});
diff --git a/test/built-ins/Temporal/Now/plainDateISO/timezone-string-datetime.js b/test/built-ins/Temporal/Now/plainDateISO/timezone-string-datetime.js
new file mode 100644
index 00000000000..4a2c2ae01f0
--- /dev/null
+++ b/test/built-ins/Temporal/Now/plainDateISO/timezone-string-datetime.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.plaindateiso
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => Temporal.Now.plainDateISO(timeZone), "bare date-time string is not a time zone");
+assert.throws(RangeError, () => Temporal.Now.plainDateISO({ timeZone }), "bare date-time string is not a time zone");
+
+// The following are all valid strings so should not throw:
+
+[
+ "2021-08-19T17:30Z",
+ "2021-08-19T17:30-07:00",
+ "2021-08-19T17:30[America/Vancouver]",
+ "2021-08-19T17:30Z[America/Vancouver]",
+ "2021-08-19T17:30-07:00[America/Vancouver]",
+].forEach((timeZone) => {
+ Temporal.Now.plainDateISO(timeZone);
+ Temporal.Now.plainDateISO({ timeZone });
+});
diff --git a/test/built-ins/Temporal/Now/plainDateTime/calendar-temporal-object.js b/test/built-ins/Temporal/Now/plainDateTime/calendar-temporal-object.js
index f94587df4b6..c87de0ef02b 100644
--- a/test/built-ins/Temporal/Now/plainDateTime/calendar-temporal-object.js
+++ b/test/built-ins/Temporal/Now/plainDateTime/calendar-temporal-object.js
@@ -18,5 +18,5 @@ features: [Temporal, arrow-function]
TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
const result = Temporal.Now.plainDateTime(temporalObject);
- assert.sameValue(result.calendar, calendar, 'The value of result.calendar is expected to equal the value of calendar');
+ assert.sameValue(result.calendar, calendar, "Temporal object coerced to calendar");
});
diff --git a/test/built-ins/Temporal/Now/plainDateTime/timezone-string-datetime.js b/test/built-ins/Temporal/Now/plainDateTime/timezone-string-datetime.js
new file mode 100644
index 00000000000..71a18e3059e
--- /dev/null
+++ b/test/built-ins/Temporal/Now/plainDateTime/timezone-string-datetime.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.plaindatetime
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => Temporal.Now.plainDateTime("iso8601", timeZone), "bare date-time string is not a time zone");
+assert.throws(RangeError, () => Temporal.Now.plainDateTime("iso8601", { timeZone }), "bare date-time string is not a time zone");
+
+// The following are all valid strings so should not throw:
+
+[
+ "2021-08-19T17:30Z",
+ "2021-08-19T17:30-07:00",
+ "2021-08-19T17:30[America/Vancouver]",
+ "2021-08-19T17:30Z[America/Vancouver]",
+ "2021-08-19T17:30-07:00[America/Vancouver]",
+].forEach((timeZone) => {
+ Temporal.Now.plainDateTime("iso8601", timeZone);
+ Temporal.Now.plainDateTime("iso8601", { timeZone });
+});
diff --git a/test/built-ins/Temporal/Now/plainDateTime/timezone.js b/test/built-ins/Temporal/Now/plainDateTime/timezone.js
new file mode 100644
index 00000000000..c2901899221
--- /dev/null
+++ b/test/built-ins/Temporal/Now/plainDateTime/timezone.js
@@ -0,0 +1,42 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.plaindatetime
+description: The value returned by TimeZone.getOffsetNanosecondsFor affects the result
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "has timeZone.timeZone",
+ "get timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+];
+
+const timeZone = new Proxy({
+ getOffsetNanosecondsFor(instant) {
+ actual.push("call timeZone.getOffsetNanosecondsFor");
+ assert.sameValue(instant instanceof Temporal.Instant, true, "Instant");
+ return -Number(instant.epochNanoseconds % 86400_000_000_000n);
+ },
+}, {
+ has(target, property) {
+ actual.push(`has timeZone.${property}`);
+ return property in target;
+ },
+ get(target, property) {
+ actual.push(`get timeZone.${property}`);
+ return target[property];
+ },
+});
+
+const calendar = Temporal.Calendar.from("iso8601");
+
+const result = Temporal.Now.plainDateTime(calendar, timeZone);
+for (const property of ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"]) {
+ assert.sameValue(result[property], 0, property);
+}
+
+assert.compareArray(actual, expected);
diff --git a/test/built-ins/Temporal/Now/plainTimeISO/length.js b/test/built-ins/Temporal/Now/plainTimeISO/length.js
new file mode 100644
index 00000000000..88563248e65
--- /dev/null
+++ b/test/built-ins/Temporal/Now/plainTimeISO/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.plaintimeiso
+description: Temporal.Now.plainTimeISO.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Now.plainTimeISO, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..e1e99140438
--- /dev/null
+++ b/test/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.plaintimeiso
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(RangeError, () => Temporal.Now.plainTimeISO(timeZone));
+});
diff --git a/test/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..e72e1cca10a
--- /dev/null
+++ b/test/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.plaintimeiso
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(RangeError, () => Temporal.Now.plainTimeISO(timeZone));
+});
diff --git a/test/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..dc9e9957ae7
--- /dev/null
+++ b/test/built-ins/Temporal/Now/plainTimeISO/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.plaintimeiso
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(TypeError, () => Temporal.Now.plainTimeISO(timeZone));
+});
diff --git a/test/built-ins/Temporal/Now/plainTimeISO/timezone-string-datetime.js b/test/built-ins/Temporal/Now/plainTimeISO/timezone-string-datetime.js
new file mode 100644
index 00000000000..1aaa5979c9a
--- /dev/null
+++ b/test/built-ins/Temporal/Now/plainTimeISO/timezone-string-datetime.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.plaintimeiso
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => Temporal.Now.plainTimeISO(timeZone), "bare date-time string is not a time zone");
+assert.throws(RangeError, () => Temporal.Now.plainTimeISO({ timeZone }), "bare date-time string is not a time zone");
+
+// The following are all valid strings so should not throw:
+
+[
+ "2021-08-19T17:30Z",
+ "2021-08-19T17:30-07:00",
+ "2021-08-19T17:30[America/Vancouver]",
+ "2021-08-19T17:30Z[America/Vancouver]",
+ "2021-08-19T17:30-07:00[America/Vancouver]",
+].forEach((timeZone) => {
+ Temporal.Now.plainTimeISO(timeZone);
+ Temporal.Now.plainTimeISO({ timeZone });
+});
diff --git a/test/built-ins/Temporal/Now/plainTimeISO/timezone.js b/test/built-ins/Temporal/Now/plainTimeISO/timezone.js
new file mode 100644
index 00000000000..81894b2a6b5
--- /dev/null
+++ b/test/built-ins/Temporal/Now/plainTimeISO/timezone.js
@@ -0,0 +1,41 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.plaintimeiso
+description: The value returned by TimeZone.getOffsetNanosecondsFor affects the result
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "has timeZone.timeZone",
+ "get timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+];
+
+const timeZone = new Proxy({
+ getOffsetNanosecondsFor(instant) {
+ actual.push("call timeZone.getOffsetNanosecondsFor");
+ assert.sameValue(instant instanceof Temporal.Instant, true, "Instant");
+ return -Number(instant.epochNanoseconds % 86400_000_000_000n);
+ },
+}, {
+ has(target, property) {
+ actual.push(`has timeZone.${property}`);
+ return property in target;
+ },
+ get(target, property) {
+ actual.push(`get timeZone.${property}`);
+ return target[property];
+ },
+});
+
+const result = Temporal.Now.plainTimeISO(timeZone);
+assert.sameValue(result instanceof Temporal.PlainTime, true);
+for (const property of ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"]) {
+ assert.sameValue(result[property], 0, property);
+}
+
+assert.compareArray(actual, expected);
diff --git a/test/built-ins/Temporal/Now/plainTimeISO/toPlainTime-override.js b/test/built-ins/Temporal/Now/plainTimeISO/toPlainTime-override.js
new file mode 100644
index 00000000000..03a856ae9ab
--- /dev/null
+++ b/test/built-ins/Temporal/Now/plainTimeISO/toPlainTime-override.js
@@ -0,0 +1,50 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.now.plaintimeiso
+description: PlainDateTime.toPlainTime is not observably called
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "has timeZone.timeZone",
+ "get timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+];
+
+Object.defineProperty(Temporal.PlainDateTime.prototype, "toPlainTime", {
+ get() {
+ actual.push("get Temporal.PlainDateTime.prototype.toPlainTime");
+ return function() {
+ actual.push("call Temporal.PlainDateTime.prototype.toPlainTime");
+ };
+ },
+});
+
+const timeZone = new Proxy({
+ getOffsetNanosecondsFor(instant) {
+ actual.push("call timeZone.getOffsetNanosecondsFor");
+ assert.sameValue(instant instanceof Temporal.Instant, true, "Instant");
+ return -Number(instant.epochNanoseconds % 86400_000_000_000n);
+ },
+}, {
+ has(target, property) {
+ actual.push(`has timeZone.${property}`);
+ return property in target;
+ },
+ get(target, property) {
+ actual.push(`get timeZone.${property}`);
+ return target[property];
+ },
+});
+
+const result = Temporal.Now.plainTimeISO(timeZone);
+assert.sameValue(result instanceof Temporal.PlainTime, true);
+for (const property of ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"]) {
+ assert.sameValue(result[property], 0, property);
+}
+
+assert.compareArray(actual, expected);
diff --git a/test/built-ins/Temporal/Now/toStringTag/prop-desc.js b/test/built-ins/Temporal/Now/toStringTag/prop-desc.js
new file mode 100644
index 00000000000..4ff032c0a5a
--- /dev/null
+++ b/test/built-ins/Temporal/Now/toStringTag/prop-desc.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-now-@@tostringtag
+description: The @@toStringTag property of Temporal.Now
+includes: [propertyHelper.js]
+features: [Symbol.toStringTag, Temporal]
+---*/
+
+verifyProperty(Temporal.Now, Symbol.toStringTag, {
+ value: "Temporal.Now",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/Now/toStringTag/string.js b/test/built-ins/Temporal/Now/toStringTag/string.js
new file mode 100644
index 00000000000..33173329cc1
--- /dev/null
+++ b/test/built-ins/Temporal/Now/toStringTag/string.js
@@ -0,0 +1,10 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-now-@@tostringtag
+description: The @@toStringTag property of Temporal.Now produces the correct value in toString
+features: [Symbol.toStringTag, Temporal]
+---*/
+
+assert.sameValue(String(Temporal.Now), "[object Temporal.Now]");
diff --git a/test/built-ins/Temporal/PlainDate/basic.js b/test/built-ins/Temporal/PlainDate/basic.js
new file mode 100644
index 00000000000..11d5b4ae78f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/basic.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: Basic tests for the PlainDate constructor.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+Object.defineProperty(Temporal.Calendar, "from", {
+ get() {
+ throw new Test262Error("Should not get Calendar.from");
+ },
+});
+
+const calendar = new Temporal.Calendar("iso8601");
+const plainDateWithObject = new Temporal.PlainDate(2020, 12, 24, calendar);
+TemporalHelpers.assertPlainDate(plainDateWithObject, 2020, 12, "M12", 24, "with object");
+assert.sameValue(plainDateWithObject.calendar, calendar);
+
+const plainDateWithString = new Temporal.PlainDate(2020, 12, 24, "iso8601");
+TemporalHelpers.assertPlainDate(plainDateWithString, 2020, 12, "M12", 24, "with string");
+assert.sameValue(plainDateWithString.calendar.toString(), "iso8601");
+assert.notSameValue(plainDateWithString.calendar, calendar);
diff --git a/test/built-ins/Temporal/PlainDate/builtin.js b/test/built-ins/Temporal/PlainDate/builtin.js
new file mode 100644
index 00000000000..65f77de24bf
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/builtin.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: Tests that Temporal.PlainDate meets the requirements for built-in objects
+info: |
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.PlainDate.prototype,
+ "object", "prototype property");
diff --git a/test/built-ins/Temporal/PlainDate/calendar-temporal-object.js b/test/built-ins/Temporal/PlainDate/calendar-temporal-object.js
new file mode 100644
index 00000000000..63f72cbc74f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/calendar-temporal-object.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindate step 5:
+ 5. Let _calendar_ be ? ToTemporalCalendarWithISODefault(_calendarLike_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const result = new Temporal.PlainDate(2000, 5, 2, temporalObject);
+ assert.sameValue(result.calendar, calendar, "Temporal object coerced to calendar");
+});
diff --git a/test/built-ins/Temporal/PlainDate/calendar-undefined.js b/test/built-ins/Temporal/PlainDate/calendar-undefined.js
new file mode 100644
index 00000000000..945fc0dd125
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/calendar-undefined.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: Calendar argument defaults to the built-in ISO 8601 calendar
+features: [Temporal]
+---*/
+
+const args = [2020, 12, 24];
+
+Object.defineProperty(Temporal.Calendar, "from", {
+ get() {
+ throw new Test262Error("Should not get Calendar.from");
+ },
+});
+
+const dateExplicit = new Temporal.PlainDate(...args, undefined);
+assert.sameValue(dateExplicit.calendar.toString(), "iso8601");
+
+const dateImplicit = new Temporal.PlainDate(...args);
+assert.sameValue(dateImplicit.calendar.toString(), "iso8601");
diff --git a/test/built-ins/Temporal/PlainDate/compare/argument-plaindatetime.js b/test/built-ins/Temporal/PlainDate/compare/argument-plaindatetime.js
new file mode 100644
index 00000000000..a41d6b0b08a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/compare/argument-plaindatetime.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.plaindate.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalDate(_one_).
+ 2. Set _two_ to ? ToTemporalDate(_two_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const result = Temporal.PlainDate.compare(datetime, date);
+ assert.sameValue(result, 0, "comparison result");
+});
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const result = Temporal.PlainDate.compare(date, datetime);
+ assert.sameValue(result, 0, "comparison result");
+});
diff --git a/test/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..ffa6b96aa56
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, Infinity, -Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(datetime, date));
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(date, datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..3d6523e833a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(datetime, date));
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(date, datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..bf475a329e3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+
+ assert.throws(TypeError, () => Temporal.PlainDate.compare(datetime, date));
+ assert.throws(TypeError, () => Temporal.PlainDate.compare(date, datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDate/compare/builtin.js b/test/built-ins/Temporal/PlainDate/compare/builtin.js
new file mode 100644
index 00000000000..1d68d6eec27
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/compare/builtin.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Tests that Temporal.PlainDate.compare meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.compare),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.compare),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.compare),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.compare.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDate/compare/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainDate/compare/calendar-fields-iterable.js
new file mode 100644
index 00000000000..e99862540f3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/compare/calendar-fields-iterable.js
@@ -0,0 +1,38 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalDate(_one_).
+ 2. Set _two_ to ? ToTemporalDate(_two_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+Temporal.PlainDate.compare(
+ { year: 2000, month: 5, day: 2, calendar: calendar1 },
+ { year: 2001, month: 6, day: 3, calendar: calendar2 },
+);
+
+assert.sameValue(calendar1.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar1.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar1.iteratorExhausted[0], "iterated through the whole iterable");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainDate/compare/calendar-temporal-object.js b/test/built-ins/Temporal/PlainDate/compare/calendar-temporal-object.js
new file mode 100644
index 00000000000..ced0874537b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/compare/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindate.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalDate(_one_).
+ 2. Set _two_ to ? ToTemporalDate(_two_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ Temporal.PlainDate.compare(
+ { year: 2000, month: 5, day: 2, calendar: temporalObject },
+ { year: 2001, month: 6, day: 3, calendar: temporalObject },
+ );
+});
diff --git a/test/built-ins/Temporal/PlainDate/compare/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDate/compare/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..787600bf3cc
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/compare/infinity-throws-rangeerror.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in a property bag for either argument is Infinity or -Infinity
+esid: sec-temporal.plaindate.compare
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const other = new Temporal.PlainDate(2000, 5, 2);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => Temporal.PlainDate.compare({ ...base, [prop]: inf }, other), `${prop} property cannot be ${inf}`);
+
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(other, { ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls1 = [];
+ const obj1 = TemporalHelpers.toPrimitiveObserver(calls1, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainDate.compare({ ...base, [prop]: obj1 }, other));
+ assert.compareArray(calls1, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+
+ const calls2 = [];
+ const obj2 = TemporalHelpers.toPrimitiveObserver(calls2, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(other, { ...base, [prop]: obj2 }));
+ assert.compareArray(calls2, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainDate/compare/length.js b/test/built-ins/Temporal/PlainDate/compare/length.js
new file mode 100644
index 00000000000..61b788ad62a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/compare/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Temporal.PlainDate.compare.length is 2
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.compare, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/compare/name.js b/test/built-ins/Temporal/PlainDate/compare/name.js
new file mode 100644
index 00000000000..0032ddbfe56
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/compare/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Temporal.PlainDate.compare.name is "compare"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.compare, "name", {
+ value: "compare",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/compare/not-a-constructor.js b/test/built-ins/Temporal/PlainDate/compare/not-a-constructor.js
new file mode 100644
index 00000000000..47fa8034060
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/compare/not-a-constructor.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: Temporal.PlainDate.compare does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.compare();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.compare), false,
+ "isConstructor(Temporal.PlainDate.compare)");
diff --git a/test/built-ins/Temporal/PlainDate/compare/prop-desc.js b/test/built-ins/Temporal/PlainDate/compare/prop-desc.js
new file mode 100644
index 00000000000..c737cfbcf31
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/compare/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.compare
+description: The "compare" property of Temporal.PlainDate
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.compare,
+ "function",
+ "`typeof PlainDate.compare` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate, "compare", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/compare/use-internal-slots.js b/test/built-ins/Temporal/PlainDate/compare/use-internal-slots.js
new file mode 100644
index 00000000000..8b0cd6af524
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/compare/use-internal-slots.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-compareisodate
+description: compare() ignores the observable properties and uses internal slots
+features: [Temporal]
+---*/
+
+function CustomError() {}
+
+class AvoidGettersDate extends Temporal.PlainDate {
+ get year() {
+ throw new CustomError();
+ }
+ get month() {
+ throw new CustomError();
+ }
+ get day() {
+ throw new CustomError();
+ }
+}
+
+const one = new AvoidGettersDate(2000, 5, 2);
+const two = new AvoidGettersDate(2006, 3, 25);
+assert.sameValue(Temporal.PlainDate.compare(one, two), -1);
diff --git a/test/built-ins/Temporal/PlainDate/constructor.js b/test/built-ins/Temporal/PlainDate/constructor.js
new file mode 100644
index 00000000000..67400c0e126
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/constructor.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: Temporal.PlainDate constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.PlainDate());
diff --git a/test/built-ins/Temporal/PlainDate/from/argument-object.js b/test/built-ins/Temporal/PlainDate/from/argument-object.js
new file mode 100644
index 00000000000..9bed48298f1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/from/argument-object.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Property bag is correctly converted into PlainDate
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateTimeFields = { year: 2019, month: 10, monthCode: "M10", day: 1, hour: 14, minute: 20, second: 36 };
+const plainDate = Temporal.PlainDate.from(dateTimeFields);
+TemporalHelpers.assertPlainDate(plainDate, 2019, 10, "M10", 1);
diff --git a/test/built-ins/Temporal/PlainDate/from/argument-plaindate.js b/test/built-ins/Temporal/PlainDate/from/argument-plaindate.js
new file mode 100644
index 00000000000..953393f824f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/from/argument-plaindate.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: A PlainDate object is copied, not returned directly
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+const result = Temporal.PlainDate.from(plainDate);
+assert.notSameValue(result, plainDate);
+TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 2);
diff --git a/test/built-ins/Temporal/PlainDate/from/argument-plaindatetime.js b/test/built-ins/Temporal/PlainDate/from/argument-plaindatetime.js
new file mode 100644
index 00000000000..b8b4b0fac31
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/from/argument-plaindatetime.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.plaindate.from step 3:
+ 3. Return ? ToTemporalDate(_item_, _options_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime, calendar) => {
+ const result = Temporal.PlainDate.from(datetime);
+ TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 2);
+ assert.sameValue(result.calendar, calendar, "calendar result");
+});
diff --git a/test/built-ins/Temporal/PlainDate/from/argument-string.js b/test/built-ins/Temporal/PlainDate/from/argument-string.js
new file mode 100644
index 00000000000..1eee15b7b17
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/from/argument-string.js
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: overflow property is extracted with string argument.
+info: |
+ 1. If Type(_item_) is Object, then
+ 1. ...
+ 1. Return ? DateFromFields(_calendar_, _fields_, _options_).
+ 1. Perform ? ToTemporalOverflow(_options_).
+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, "reject", "overflow");
+ }
+};
+
+const result = Temporal.PlainDate.from("2021-05-17", object);
+assert.compareArray(actual, expected, "Successful call");
+TemporalHelpers.assertPlainDate(result, 2021, 5, "M05", 17);
+
+actual.splice(0, actual.length); // empty it for the next check
+assert.throws(RangeError, () => Temporal.PlainDate.from(7, object));
+assert.compareArray(actual, expected, "Failing call");
diff --git a/test/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..2564660e585
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => Temporal.PlainDate.from(datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..a1d2f1b7d59
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => Temporal.PlainDate.from(datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..9866661ac0b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => Temporal.PlainDate.from(datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDate/from/builtin.js b/test/built-ins/Temporal/PlainDate/from/builtin.js
new file mode 100644
index 00000000000..84a3f44334d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/from/builtin.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Tests that Temporal.PlainDate.from meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.from.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDate/from/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainDate/from/calendar-fields-iterable.js
new file mode 100644
index 00000000000..a693fe09bc2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/from/calendar-fields-iterable.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.from step 3:
+ 3. Return ? ToTemporalDate(_item_, _options_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+Temporal.PlainDate.from({ year: 2000, month: 5, day: 2, calendar });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainDate/from/calendar-temporal-object.js b/test/built-ins/Temporal/PlainDate/from/calendar-temporal-object.js
new file mode 100644
index 00000000000..47a50806141
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/from/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindate.from step 3:
+ 3. Return ? ToTemporalDate(_item_, _options_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const result = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+ assert.sameValue(result.calendar, calendar, "Temporal object coerced to calendar");
+});
diff --git a/test/built-ins/Temporal/PlainDate/from/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDate/from/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..cb4ab5c7260
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/from/infinity-throws-rangeerror.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindate.from
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => Temporal.PlainDate.from({ ...base, [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainDate.from({ ...base, [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
diff --git a/test/built-ins/Temporal/PlainDate/from/length.js b/test/built-ins/Temporal/PlainDate/from/length.js
new file mode 100644
index 00000000000..ddf1c942c6a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/from/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Temporal.PlainDate.from.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/from/name.js b/test/built-ins/Temporal/PlainDate/from/name.js
new file mode 100644
index 00000000000..73104861517
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/from/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Temporal.PlainDate.from.name is "from"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/from/not-a-constructor.js b/test/built-ins/Temporal/PlainDate/from/not-a-constructor.js
new file mode 100644
index 00000000000..00f77b3ced9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/from/not-a-constructor.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Temporal.PlainDate.from does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.from), false,
+ "isConstructor(Temporal.PlainDate.from)");
diff --git a/test/built-ins/Temporal/PlainDate/from/options-invalid.js b/test/built-ins/Temporal/PlainDate/from/options-invalid.js
new file mode 100644
index 00000000000..c3f95ff1d58
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/from/options-invalid.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: TypeError thrown when a primitive is passed as the options argument
+features: [Temporal]
+---*/
+
+const fields = { year: 2000, month: 13, day: 2 };
+
+const values = [null, true, "hello", Symbol("foo"), 1, 1n];
+for (const badOptions of values) {
+ assert.throws(TypeError, () => Temporal.PlainDate.from(fields, badOptions));
+}
diff --git a/test/built-ins/Temporal/PlainDate/from/options-undefined.js b/test/built-ins/Temporal/PlainDate/from/options-undefined.js
new file mode 100644
index 00000000000..b2c7ba0cb71
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/from/options-undefined.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const fields = { year: 2000, month: 13, day: 2 };
+
+const explicit = Temporal.PlainDate.from(fields, undefined);
+assert.sameValue(explicit.month, 12, "default overflow is constrain");
+
+const implicit = Temporal.PlainDate.from(fields);
+assert.sameValue(implicit.month, 12, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainDate/from/order-of-operations.js b/test/built-ins/Temporal/PlainDate/from/order-of-operations.js
new file mode 100644
index 00000000000..21d8fb5182e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/from/order-of-operations.js
@@ -0,0 +1,48 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Properties on an object passed to from() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get calendar",
+ "get day",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get month",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+];
+const actual = [];
+const fields = {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+ day: 1.7,
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ if (key === "calendar") return Temporal.Calendar.from("iso8601");
+ const result = target[key];
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = Temporal.PlainDate.from(argument);
+TemporalHelpers.assertPlainDate(result, 1, 1, "M01", 1);
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainDate/from/overflow-invalid-string.js b/test/built-ins/Temporal/PlainDate/from/overflow-invalid-string.js
new file mode 100644
index 00000000000..01f5c913eeb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/from/overflow-invalid-string.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporaldate steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ g. Return ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalDate]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalDate(_item_, _options_).
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainDate(2000, 5, 2),
+ { year: 2000, month: 5, day: 2 },
+ "2000-05-02",
+];
+validValues.forEach((value) => {
+ assert.throws(RangeError, () => Temporal.PlainDate.from(value, { overflow: "other string" }));
+});
diff --git a/test/built-ins/Temporal/PlainDate/from/overflow-undefined.js b/test/built-ins/Temporal/PlainDate/from/overflow-undefined.js
new file mode 100644
index 00000000000..8e755c5eea3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/from/overflow-undefined.js
@@ -0,0 +1,41 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporaldate steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ g. Return ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalDate]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalDate(_item_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainDate(2000, 5, 2),
+ "2000-05-02",
+];
+validValues.forEach((value) => {
+ const explicit = Temporal.PlainDate.from(value, { overflow: undefined });
+ TemporalHelpers.assertPlainDate(explicit, 2000, 5, "M05", 2, "overflow is ignored");
+ const implicit = Temporal.PlainDate.from(value, {});
+ TemporalHelpers.assertPlainDate(implicit, 2000, 5, "M05", 2, "overflow is ignored");
+});
+
+const propertyBag = { year: 2000, month: 13, day: 34 };
+const explicit = Temporal.PlainDate.from(propertyBag, { overflow: undefined });
+TemporalHelpers.assertPlainDate(explicit, 2000, 12, "M12", 31, "default overflow is constrain");
+const implicit = Temporal.PlainDate.from(propertyBag, {});
+TemporalHelpers.assertPlainDate(implicit, 2000, 12, "M12", 31, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainDate/from/overflow-wrong-type.js b/test/built-ins/Temporal/PlainDate/from/overflow-wrong-type.js
new file mode 100644
index 00000000000..1a2eb7f4bfd
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/from/overflow-wrong-type.js
@@ -0,0 +1,34 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporaldate steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ g. Return ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalDate]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalDate(_item_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainDate(2000, 5, 2),
+ { year: 2000, month: 5, day: 2 },
+ "2000-05-02",
+];
+validValues.forEach((value) => TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => Temporal.PlainDate.from(value, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 2, descr),
+));
diff --git a/test/built-ins/Temporal/PlainDate/from/prop-desc.js b/test/built-ins/Temporal/PlainDate/from/prop-desc.js
new file mode 100644
index 00000000000..a19e3f4c092
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/from/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: The "from" property of Temporal.PlainDate
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.from,
+ "function",
+ "`typeof PlainDate.from` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/from/subclassing-ignored.js b/test/built-ins/Temporal/PlainDate/from/subclassing-ignored.js
new file mode 100644
index 00000000000..6e4cf1d8baa
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/from/subclassing-ignored.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.PlainDate,
+ "from",
+ ["2000-05-02"],
+ (result) => TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 2),
+);
diff --git a/test/built-ins/Temporal/PlainDate/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDate/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..f6b7e98fee0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/infinity-throws-rangeerror.js
@@ -0,0 +1,39 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDate throws a RangeError if any value is Infinity
+esid: sec-temporal.plaindate
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.PlainDate(Infinity, 1, 1));
+assert.throws(RangeError, () => new Temporal.PlainDate(1970, Infinity, 1));
+assert.throws(RangeError, () => new Temporal.PlainDate(1970, 1, Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite year",
+ [O(Infinity, "year"), O(1, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf"]
+ ],
+ [
+ "infinite month",
+ [O(2, "year"), O(Infinity, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf"]
+ ],
+ [
+ "infinite day",
+ [O(2, "year"), O(1, "month"), O(Infinity, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainDate(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
diff --git a/test/built-ins/Temporal/PlainDate/length.js b/test/built-ins/Temporal/PlainDate/length.js
new file mode 100644
index 00000000000..0291a35e1cb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: Temporal.PlainDate.length is 3
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate, "length", {
+ value: 3,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/missing-arguments.js b/test/built-ins/Temporal/PlainDate/missing-arguments.js
new file mode 100644
index 00000000000..aa8559e8b81
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/missing-arguments.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: RangeError thrown when constructor invoked with no argument
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "valueOf year",
+ "valueOf month",
+];
+const actual = [];
+const args = [
+ { valueOf() { actual.push("valueOf year"); return 1; } },
+ { valueOf() { actual.push("valueOf month"); return 1; } },
+];
+
+assert.throws(RangeError, () => new Temporal.PlainDate(...args));
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainDate/name.js b/test/built-ins/Temporal/PlainDate/name.js
new file mode 100644
index 00000000000..5c2e807260f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: Temporal.PlainDate.name is "PlainDate"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate, "name", {
+ value: "PlainDate",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/negative-infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDate/negative-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..3eb3c4c7c11
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,39 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDate throws a RangeError if any value is -Infinity
+esid: sec-temporal.plaindate
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.PlainDate(-Infinity, 1, 1));
+assert.throws(RangeError, () => new Temporal.PlainDate(1970, -Infinity, 1));
+assert.throws(RangeError, () => new Temporal.PlainDate(1970, 1, -Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite year",
+ [O(-Infinity, "year"), O(1, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf"]
+ ],
+ [
+ "infinite month",
+ [O(2, "year"), O(-Infinity, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf"]
+ ],
+ [
+ "infinite day",
+ [O(2, "year"), O(1, "month"), O(-Infinity, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainDate(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
diff --git a/test/built-ins/Temporal/PlainDate/prop-desc.js b/test/built-ins/Temporal/PlainDate/prop-desc.js
new file mode 100644
index 00000000000..dfba70db579
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate
+description: The "PlainDate" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate,
+ "function",
+ "`typeof PlainDate` is `function`"
+);
+
+verifyProperty(Temporal, "PlainDate", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/add/argument-not-object.js b/test/built-ins/Temporal/PlainDate/prototype/add/argument-not-object.js
new file mode 100644
index 00000000000..6a88e4b19ef
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/add/argument-not-object.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Passing a primitive other than string to add() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+assert.throws(RangeError, () => instance.add(undefined), "undefined");
+assert.throws(RangeError, () => instance.add(null), "null");
+assert.throws(RangeError, () => instance.add(true), "boolean");
+assert.throws(RangeError, () => instance.add(""), "empty string");
+assert.throws(TypeError, () => instance.add(Symbol()), "Symbol");
+assert.throws(RangeError, () => instance.add(7), "number");
+assert.throws(RangeError, () => instance.add(7n), "bigint");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/add/argument-string-negative-fractional-units.js b/test/built-ins/Temporal/PlainDate/prototype/add/argument-string-negative-fractional-units.js
new file mode 100644
index 00000000000..6a3933fd61f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/add/argument-string-negative-fractional-units.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const resultHours = instance.add("-PT24.567890123H");
+TemporalHelpers.assertPlainDate(resultHours, 2000, 5, "M05", 1, "negative fractional hours");
+
+const resultMinutes = instance.add("-PT1440.567890123M");
+TemporalHelpers.assertPlainDate(resultMinutes, 2000, 5, "M05", 1, "negative fractional minutes");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/add/argument-string.js b/test/built-ins/Temporal/PlainDate/prototype/add/argument-string.js
new file mode 100644
index 00000000000..5abb43791bf
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/add/argument-string.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+const result = instance.add("P3D");
+TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 5);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units.js b/test/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units.js
new file mode 100644
index 00000000000..b0c6bd878ac
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units.js
@@ -0,0 +1,51 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Durations with units smaller than days are balanced before adding, in the calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+
+class DateAddCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateAdd(date, duration, options) {
+ actual.push(duration);
+ return super.dateAdd(date, duration, options);
+ }
+}
+
+const calendar = new DateAddCalendar();
+const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+const duration = new Temporal.Duration(0, 0, 0, 1, 24, 1440, 86400, 86400_000, 86400_000_000, 86400_000_000_000);
+
+const result = date.add(duration);
+TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 9, "units smaller than days are balanced");
+
+assert.sameValue(actual.length, 1, "calendar.dateAdd called exactly once");
+assert.sameValue(actual[0], duration, "the duration is passed directly to the calendar");
+
+const resultString = date.add("P1DT24H1440M86400S");
+TemporalHelpers.assertPlainDate(resultString, 2000, 5, "M05", 6, "units smaller than days are balanced");
+
+assert.sameValue(actual.length, 2, "calendar.dateAdd called exactly once");
+TemporalHelpers.assertDuration(actual[1], 0, 0, 0, 1, 24, 1440, 86400, 0, 0, 0, "the duration is not balanced before passing to the calendar");
+
+const resultPropBag = date.add({ days: 1, hours: 24, minutes: 1440, seconds: 86400, milliseconds: 86400_000, microseconds: 86400_000_000, nanoseconds: 86400_000_000_000 });
+TemporalHelpers.assertPlainDate(resultPropBag, 2000, 5, "M05", 9, "units smaller than days are balanced");
+
+assert.sameValue(actual.length, 3, "calendar.dateAdd called exactly once");
+TemporalHelpers.assertDuration(actual[2], 0, 0, 0, 1, 24, 1440, 86400, 86400_000, 86400_000_000, 86400_000_000_000, "the duration is not balanced before passing to the calendar");
+
+const negativeDuration = new Temporal.Duration(0, 0, 0, -1, -24, -1440, -86400, -86400_000, -86400_000_000, -86400_000_000_000);
+const resultNegative = date.add(negativeDuration);
+TemporalHelpers.assertPlainDate(resultNegative, 2000, 4, "M04", 25, "units smaller than days are balanced");
+
+assert.sameValue(actual.length, 4, "calendar.dateAdd called exactly once");
+assert.sameValue(actual[3], negativeDuration, "the duration is passed directly to the calendar");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/add/builtin.js b/test/built-ins/Temporal/PlainDate/prototype/add/builtin.js
new file mode 100644
index 00000000000..1a623151bd9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/add/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: >
+ Tests that Temporal.PlainDate.prototype.add
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.add),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.add),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.add),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.add.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/add/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDate/prototype/add/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..9f5d6850cb9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/add/infinity-throws-rangeerror.js
@@ -0,0 +1,35 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDate.prototype.add throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.plaindate.prototype.add
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/add/length.js b/test/built-ins/Temporal/PlainDate/prototype/add/length.js
new file mode 100644
index 00000000000..73b5068793b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/add/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Temporal.PlainDate.prototype.add.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.add, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/add/name.js b/test/built-ins/Temporal/PlainDate/prototype/add/name.js
new file mode 100644
index 00000000000..302109faa03
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/add/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Temporal.PlainDate.prototype.add.name is "add".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.add, "name", {
+ value: "add",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/add/negative-infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDate/prototype/add/negative-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..f50ae005ad2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/add/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,35 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDate.prototype.add throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.plaindate.prototype.add
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: -Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/add/non-integer-throws-rangeerror.js b/test/built-ins/Temporal/PlainDate/prototype/add/non-integer-throws-rangeerror.js
new file mode 100644
index 00000000000..0125f388c39
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/add/non-integer-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.add({ [field]: -1.5 }));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/add/not-a-constructor.js b/test/built-ins/Temporal/PlainDate/prototype/add/not-a-constructor.js
new file mode 100644
index 00000000000..ba78130f349
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/add/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: >
+ Temporal.PlainDate.prototype.add does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.add();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.add), false,
+ "isConstructor(Temporal.PlainDate.prototype.add)");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/add/options-invalid.js b/test/built-ins/Temporal/PlainDate/prototype/add/options-invalid.js
new file mode 100644
index 00000000000..67889443704
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/add/options-invalid.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: TypeError thrown when a primitive is passed as the options argument
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 1, 31);
+const duration = { months: 1 };
+
+const values = [null, true, "hello", Symbol("foo"), 1, 1n];
+for (const badOptions of values) {
+ assert.throws(TypeError, () => plainDate.add(duration, badOptions));
+}
diff --git a/test/built-ins/Temporal/PlainDate/prototype/add/options-undefined.js b/test/built-ins/Temporal/PlainDate/prototype/add/options-undefined.js
new file mode 100644
index 00000000000..df27f6e5af9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/add/options-undefined.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 1, 31);
+const duration = { months: 1 };
+
+const explicit = date.add(duration, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = date.add(duration);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/add/order-of-operations.js b/test/built-ins/Temporal/PlainDate/prototype/add/order-of-operations.js
new file mode 100644
index 00000000000..4576c59db53
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/add/order-of-operations.js
@@ -0,0 +1,74 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Properties on an object passed to add() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const expected = [
+ "get days",
+ "get days.valueOf",
+ "call days.valueOf",
+ "get hours",
+ "get hours.valueOf",
+ "call hours.valueOf",
+ "get microseconds",
+ "get microseconds.valueOf",
+ "call microseconds.valueOf",
+ "get milliseconds",
+ "get milliseconds.valueOf",
+ "call milliseconds.valueOf",
+ "get minutes",
+ "get minutes.valueOf",
+ "call minutes.valueOf",
+ "get months",
+ "get months.valueOf",
+ "call months.valueOf",
+ "get nanoseconds",
+ "get nanoseconds.valueOf",
+ "call nanoseconds.valueOf",
+ "get seconds",
+ "get seconds.valueOf",
+ "call seconds.valueOf",
+ "get weeks",
+ "get weeks.valueOf",
+ "call weeks.valueOf",
+ "get years",
+ "get years.valueOf",
+ "call years.valueOf",
+];
+const actual = [];
+const fields = {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = instance.add(argument);
+TemporalHelpers.assertPlainDate(result, 2001, 6, "M06", 10);
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/add/overflow-invalid-string.js b/test/built-ins/Temporal/PlainDate/prototype/add/overflow-invalid-string.js
new file mode 100644
index 00000000000..e684db9877b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/add/overflow-invalid-string.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.add step 7:
+ 7. Return ? CalendarDateAdd(_temporalDate_.[[Calendar]], _temporalDate_, _balancedDuration_, _options_).
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const duration = new Temporal.Duration(3, 3, 0, 3);
+assert.throws(RangeError, () => date.add(duration, { overflow: "other string" }));
diff --git a/test/built-ins/Temporal/PlainDate/prototype/add/overflow-undefined.js b/test/built-ins/Temporal/PlainDate/prototype/add/overflow-undefined.js
new file mode 100644
index 00000000000..64abf666c19
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/add/overflow-undefined.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.add step 7:
+ 7. Return ? CalendarDateAdd(_temporalDate_.[[Calendar]], _temporalDate_, _balancedDuration_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 31);
+const duration = new Temporal.Duration(3, 1);
+
+const explicit = date.add(duration, { overflow: undefined });
+TemporalHelpers.assertPlainDate(explicit, 2003, 6, "M06", 30, "default overflow is constrain");
+const implicit = date.add(duration, {});
+TemporalHelpers.assertPlainDate(implicit, 2003, 6, "M06", 30, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/add/overflow-wrong-type.js b/test/built-ins/Temporal/PlainDate/prototype/add/overflow-wrong-type.js
new file mode 100644
index 00000000000..aa8b95c0656
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/add/overflow-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.add step 7:
+ 7. Return ? CalendarDateAdd(_temporalDate_.[[Calendar]], _temporalDate_, _balancedDuration_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const duration = new Temporal.Duration(3, 3, 0, 3);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => date.add(duration, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDate(result, 2003, 8, "M08", 5, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/add/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/add/prop-desc.js
new file mode 100644
index 00000000000..3cf4236d042
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/add/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: The "add" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.add,
+ "function",
+ "`typeof PlainDate.prototype.add` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "add", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/add/subclassing-ignored.js b/test/built-ins/Temporal/PlainDate/prototype/add/subclassing-ignored.js
new file mode 100644
index 00000000000..792cfff6e07
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/add/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.add
+description: Objects of a subclass are never created as return values for add()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDate,
+ [2000, 5, 2],
+ "add",
+ [{ days: 1 }],
+ (result) => TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 3),
+);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/calendar/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/calendar/prop-desc.js
new file mode 100644
index 00000000000..6a40d4aa3c4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/calendar/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.calendar
+description: The "calendar" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "calendar");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/day/calendar-returns-infinity.js b/test/built-ins/Temporal/PlainDate/prototype/day/calendar-returns-infinity.js
new file mode 100644
index 00000000000..f13b182d965
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/day/calendar-returns-infinity.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.day
+description: Getter throws if the calendar returns ±∞ from its day method
+features: [Temporal]
+---*/
+
+class InfinityCalendar extends Temporal.Calendar {
+ constructor(positive) {
+ super("iso8601");
+ this.positive = positive;
+ }
+
+ day() {
+ return this.positive ? Infinity : -Infinity;
+ }
+}
+
+const pos = new InfinityCalendar(true);
+const instance1 = new Temporal.PlainDate(2000, 5, 2, pos);
+assert.throws(RangeError, () => instance1.day);
+
+const neg = new InfinityCalendar(false);
+const instance2 = new Temporal.PlainDate(2000, 5, 2, neg);
+assert.throws(RangeError, () => instance2.day);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/day/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/day/prop-desc.js
new file mode 100644
index 00000000000..28a23c8f7b2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/day/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.day
+description: The "day" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "day");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/dayOfWeek/basic.js b/test/built-ins/Temporal/PlainDate/prototype/dayOfWeek/basic.js
new file mode 100644
index 00000000000..65ee0499b38
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/dayOfWeek/basic.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.dayofweek
+description: Basic tests for dayOfWeek().
+features: [Temporal]
+---*/
+
+for (let i = 1; i <= 7; ++i) {
+ const plainDate = new Temporal.PlainDate(1976, 11, 14 + i);
+ assert.sameValue(plainDate.dayOfWeek, i, `${plainDate} should be on day ${i}`);
+}
diff --git a/test/built-ins/Temporal/PlainDate/prototype/dayOfWeek/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/dayOfWeek/prop-desc.js
new file mode 100644
index 00000000000..1936b5e477b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/dayOfWeek/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.dayofweek
+description: The "dayOfWeek" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "dayOfWeek");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/dayOfYear/basic.js b/test/built-ins/Temporal/PlainDate/prototype/dayOfYear/basic.js
new file mode 100644
index 00000000000..be771cc0575
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/dayOfYear/basic.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.dayofyear
+description: Basic tests for dayOfYear().
+features: [Temporal]
+---*/
+
+for (let i = 1; i <= 7; ++i) {
+ const plainDate = new Temporal.PlainDate(1976, 11, 14 + i);
+ assert.sameValue(plainDate.dayOfYear, 319 + i, `${plainDate} should be on day ${319 + i}`);
+}
diff --git a/test/built-ins/Temporal/PlainDate/prototype/dayOfYear/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/dayOfYear/prop-desc.js
new file mode 100644
index 00000000000..116af39764a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/dayOfYear/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.dayofyear
+description: The "dayOfYear" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "dayOfYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/daysInMonth/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/daysInMonth/prop-desc.js
new file mode 100644
index 00000000000..95178057784
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/daysInMonth/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.daysinmonth
+description: The "daysInMonth" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "daysInMonth");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/daysInWeek/basic.js b/test/built-ins/Temporal/PlainDate/prototype/daysInWeek/basic.js
new file mode 100644
index 00000000000..c3eccf9a107
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/daysInWeek/basic.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.daysinweek
+description: Basic tests for daysInWeek().
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1976, 11, 18);
+assert.sameValue(plainDate.daysInWeek, 7);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/daysInWeek/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/daysInWeek/prop-desc.js
new file mode 100644
index 00000000000..5f1ce2c207a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/daysInWeek/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.daysinweek
+description: The "daysInWeek" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "daysInWeek");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/daysInYear/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/daysInYear/prop-desc.js
new file mode 100644
index 00000000000..1203f820356
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/daysInYear/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.daysinyear
+description: The "daysInYear" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "daysInYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/equals/argument-plaindatetime.js b/test/built-ins/Temporal/PlainDate/prototype/equals/argument-plaindatetime.js
new file mode 100644
index 00000000000..ba55013f645
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/equals/argument-plaindatetime.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.plaindate.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ assert(date.equals(datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/equals/argument-wrong-type.js b/test/built-ins/Temporal/PlainDate/prototype/equals/argument-wrong-type.js
new file mode 100644
index 00000000000..2e8088d7676
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/equals/argument-wrong-type.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Appropriate error thrown when argument cannot be converted to a valid string
+features: [Symbol, Temporal]
+---*/
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+
+assert.throws(RangeError, () => instance.equals(undefined), "undefined");
+assert.throws(RangeError, () => instance.equals(null), "null");
+assert.throws(RangeError, () => instance.equals(true), "true");
+assert.throws(RangeError, () => instance.equals(""), "empty string");
+assert.throws(TypeError, () => instance.equals(Symbol()), "symbol");
+assert.throws(RangeError, () => instance.equals(1), "1");
+assert.throws(TypeError, () => instance.equals({}), "plain object");
+assert.throws(TypeError, () => instance.equals(Temporal.PlainDate), "Temporal.PlainDate");
+assert.throws(TypeError, () => instance.equals(Temporal.PlainDate.prototype), "Temporal.PlainDate.prototype");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..8eede893fca
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.equals(datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..c659f873460
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.equals(datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..c7e10d641aa
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => date.equals(datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/equals/builtin.js b/test/built-ins/Temporal/PlainDate/prototype/equals/builtin.js
new file mode 100644
index 00000000000..f8d24436853
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/equals/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: >
+ Tests that Temporal.PlainDate.prototype.equals
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.equals),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.equals),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.equals),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.equals.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/equals/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainDate/prototype/equals/calendar-fields-iterable.js
new file mode 100644
index 00000000000..2d4575071e9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/equals/calendar-fields-iterable.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const date = new Temporal.PlainDate(2000, 5, 2, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+date.equals({ year: 2005, month: 6, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/equals/calendar-temporal-object.js b/test/built-ins/Temporal/PlainDate/prototype/equals/calendar-temporal-object.js
new file mode 100644
index 00000000000..7c0d150b549
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/equals/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindate.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const date = new Temporal.PlainDate(2000, 5, 2, temporalObject);
+ date.equals({ year: 2005, month: 6, day: 2, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/equals/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDate/prototype/equals/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..594555e7758
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/equals/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindate.prototype.equals
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/equals/length.js b/test/built-ins/Temporal/PlainDate/prototype/equals/length.js
new file mode 100644
index 00000000000..39cb50a5769
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/equals/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Temporal.PlainDate.prototype.equals.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.equals, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/equals/name.js b/test/built-ins/Temporal/PlainDate/prototype/equals/name.js
new file mode 100644
index 00000000000..82462da0945
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/equals/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: Temporal.PlainDate.prototype.equals.name is "equals".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.equals, "name", {
+ value: "equals",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/equals/not-a-constructor.js b/test/built-ins/Temporal/PlainDate/prototype/equals/not-a-constructor.js
new file mode 100644
index 00000000000..753f944e172
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/equals/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: >
+ Temporal.PlainDate.prototype.equals does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.equals();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.equals), false,
+ "isConstructor(Temporal.PlainDate.prototype.equals)");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/equals/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/equals/prop-desc.js
new file mode 100644
index 00000000000..a3257925245
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/equals/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.equals
+description: The "equals" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.equals,
+ "function",
+ "`typeof PlainDate.prototype.equals` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "equals", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/getISOFields/builtin.js b/test/built-ins/Temporal/PlainDate/prototype/getISOFields/builtin.js
new file mode 100644
index 00000000000..1d51121d4d0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/getISOFields/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getisofields
+description: >
+ Tests that Temporal.PlainDate.prototype.getISOFields
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.getISOFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.getISOFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.getISOFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.getISOFields.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/getISOFields/field-names.js b/test/built-ins/Temporal/PlainDate/prototype/getISOFields/field-names.js
new file mode 100644
index 00000000000..1b8e5a94441
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/getISOFields/field-names.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getisofields
+description: Correct field names on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+
+const result = date.getISOFields();
+assert.sameValue(result.isoYear, 2000, "isoYear result");
+assert.sameValue(result.isoMonth, 5, "isoMonth result");
+assert.sameValue(result.isoDay, 2, "isoDay result");
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/getISOFields/field-prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/getISOFields/field-prop-desc.js
new file mode 100644
index 00000000000..139af2c021f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/getISOFields/field-prop-desc.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getisofields
+description: Properties on the returned object have the correct descriptor
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoMonth",
+ "isoYear",
+];
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const result = date.getISOFields();
+
+for (const property of expected) {
+ verifyProperty(result, property, {
+ writable: true,
+ enumerable: true,
+ configurable: true,
+ });
+}
diff --git a/test/built-ins/Temporal/PlainDate/prototype/getISOFields/field-traversal-order.js b/test/built-ins/Temporal/PlainDate/prototype/getISOFields/field-traversal-order.js
new file mode 100644
index 00000000000..10b631991c6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/getISOFields/field-traversal-order.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getisofields
+description: Properties added in correct order to object returned from getISOFields
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoMonth",
+ "isoYear",
+];
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const result = date.getISOFields();
+
+assert.compareArray(Object.keys(result), expected);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/getISOFields/length.js b/test/built-ins/Temporal/PlainDate/prototype/getISOFields/length.js
new file mode 100644
index 00000000000..679f49fec83
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/getISOFields/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getisofields
+description: Temporal.PlainDate.prototype.getISOFields.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.getISOFields, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/getISOFields/name.js b/test/built-ins/Temporal/PlainDate/prototype/getISOFields/name.js
new file mode 100644
index 00000000000..67e6a28e508
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/getISOFields/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getisofields
+description: Temporal.PlainDate.prototype.getISOFields.name is "getISOFields".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.getISOFields, "name", {
+ value: "getISOFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/getISOFields/not-a-constructor.js b/test/built-ins/Temporal/PlainDate/prototype/getISOFields/not-a-constructor.js
new file mode 100644
index 00000000000..265576e0cb3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/getISOFields/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getisofields
+description: >
+ Temporal.PlainDate.prototype.getISOFields does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.getISOFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.getISOFields), false,
+ "isConstructor(Temporal.PlainDate.prototype.getISOFields)");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/getISOFields/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/getISOFields/prop-desc.js
new file mode 100644
index 00000000000..89d42869c1a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/getISOFields/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.getisofields
+description: The "getISOFields" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.getISOFields,
+ "function",
+ "`typeof PlainDate.prototype.getISOFields` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "getISOFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/inLeapYear/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/inLeapYear/prop-desc.js
new file mode 100644
index 00000000000..70bed354ce6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/inLeapYear/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.inleapyear
+description: The "inLeapYear" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "inLeapYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/month/calendar-returns-infinity.js b/test/built-ins/Temporal/PlainDate/prototype/month/calendar-returns-infinity.js
new file mode 100644
index 00000000000..572dbfcea80
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/month/calendar-returns-infinity.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.month
+description: Getter throws if the calendar returns ±∞ from its month method
+features: [Temporal]
+---*/
+
+class InfinityCalendar extends Temporal.Calendar {
+ constructor(positive) {
+ super("iso8601");
+ this.positive = positive;
+ }
+
+ month() {
+ return this.positive ? Infinity : -Infinity;
+ }
+}
+
+const pos = new InfinityCalendar(true);
+const instance1 = new Temporal.PlainDate(2000, 5, 2, pos);
+assert.throws(RangeError, () => instance1.month);
+
+const neg = new InfinityCalendar(false);
+const instance2 = new Temporal.PlainDate(2000, 5, 2, neg);
+assert.throws(RangeError, () => instance2.month);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/month/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/month/prop-desc.js
new file mode 100644
index 00000000000..f65db7818f5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/month/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.month
+description: The "month" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "month");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/monthCode/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/monthCode/prop-desc.js
new file mode 100644
index 00000000000..f5d7e182e39
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/monthCode/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.monthcode
+description: The "monthCode" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "monthCode");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/monthsInYear/basic.js b/test/built-ins/Temporal/PlainDate/prototype/monthsInYear/basic.js
new file mode 100644
index 00000000000..96cb9b67522
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/monthsInYear/basic.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.monthsinyear
+description: Basic tests for monthsInYear().
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1976, 11, 18);
+assert.sameValue(plainDate.monthsInYear, 12);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/monthsInYear/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/monthsInYear/prop-desc.js
new file mode 100644
index 00000000000..d591b01b6b0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/monthsInYear/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.monthsinyear
+description: The "monthsInYear" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "monthsInYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/argument-plaindatetime.js b/test/built-ins/Temporal/PlainDate/prototype/since/argument-plaindatetime.js
new file mode 100644
index 00000000000..7ae43e07fe3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/argument-plaindatetime.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.since
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.plaindate.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const result = date.since(datetime);
+ assert.sameValue(result.total({ unit: "nanoseconds" }), 0, "time part dropped");
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..ce35b550d62
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.since(datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..6ae628b48e2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.since(datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..0a3497025f8
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => date.since(datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/basic.js b/test/built-ins/Temporal/PlainDate/prototype/since/basic.js
new file mode 100644
index 00000000000..25ef7d7c2b5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/basic.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Basic tests for since().
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1969, 7, 24);
+const plainDate2 = Temporal.PlainDate.from({ year: 1969, month: 10, day: 5 });
+TemporalHelpers.assertDuration(plainDate2.since(plainDate), 0, 0, 0, /* days = */ 73, 0, 0, 0, 0, 0, 0, "same year");
+
+const earlier = new Temporal.PlainDate(1969, 7, 24);
+const later = new Temporal.PlainDate(1996, 3, 3);
+const duration = later.since(earlier);
+TemporalHelpers.assertDuration(duration, 0, 0, 0, /* days = */ 9719, 0, 0, 0, 0, 0, 0, "different year");
+
+TemporalHelpers.assertDuration(plainDate.since({ year: 2019, month: 7, day: 24 }), 0, 0, 0, /* days = */ -18262, 0, 0, 0, 0, 0, 0, "option bag");
+TemporalHelpers.assertDuration(plainDate.since("2019-07-24"), 0, 0, 0, /* days = */ -18262, 0, 0, 0, 0, 0, 0, "string");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/builtin.js b/test/built-ins/Temporal/PlainDate/prototype/since/builtin.js
new file mode 100644
index 00000000000..e739c110cfd
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: >
+ Tests that Temporal.PlainDate.prototype.since
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.since),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.since),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.since),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.since.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js b/test/built-ins/Temporal/PlainDate/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 00000000000..1d52ad20465
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.plaindate.prototype.since steps 13–14:
+ 13. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _largestUnit_).
+ 14. Let _result_ be ? CalendarDateUntil(_temporalDate_.[[Calendar]], _other_, _temporalDate_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.PlainDate(2000, 5, 2, calendar);
+ const later = new Temporal.PlainDate(2001, 6, 3, calendar);
+ later.since(earlier, { largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"],
+ weeks: ["week"],
+ days: ["day"]
+ }
+);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainDate/prototype/since/calendar-fields-iterable.js
new file mode 100644
index 00000000000..0c66704c388
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/calendar-fields-iterable.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const date = new Temporal.PlainDate(2000, 5, 2, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+date.since({ year: 2005, month: 6, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/calendar-id-match.js b/test/built-ins/Temporal/PlainDate/prototype/since/calendar-id-match.js
new file mode 100644
index 00000000000..0c2953910ca
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/calendar-id-match.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Calculation is performed if calendars' toString results match
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class Calendar1 extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ toString() {
+ return "A";
+ }
+}
+class Calendar2 extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ toString() {
+ return "A";
+ }
+}
+
+const plainDate1 = new Temporal.PlainDate(2000, 1, 1, new Calendar1());
+const plainDate2 = new Temporal.PlainDate(2000, 1, 2, new Calendar2());
+TemporalHelpers.assertDuration(plainDate2.since(plainDate1), 0, 0, 0, /* days = */ 1, 0, 0, 0, 0, 0, 0);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/calendar-mismatch.js b/test/built-ins/Temporal/PlainDate/prototype/since/calendar-mismatch.js
new file mode 100644
index 00000000000..eabfb22ad7c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/calendar-mismatch.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: RangeError thrown if calendars' toString results do not match
+features: [Temporal]
+---*/
+
+const calendar1 = { toString() { return "A"; } };
+const calendar2 = { toString() { return "B"; } };
+
+const plainDate1 = new Temporal.PlainDate(2000, 1, 1, calendar1);
+const plainDate2 = new Temporal.PlainDate(2000, 1, 1, calendar2);
+assert.throws(RangeError, () => plainDate1.since(plainDate2));
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/calendar-temporal-object.js b/test/built-ins/Temporal/PlainDate/prototype/since/calendar-temporal-object.js
new file mode 100644
index 00000000000..333aee1f873
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindate.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const date = new Temporal.PlainDate(2000, 5, 2, temporalObject);
+ date.since({ year: 2005, month: 6, day: 2, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDate/prototype/since/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..221390ad164
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindate.prototype.since
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.since({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.since({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/largestunit-default.js b/test/built-ins/Temporal/PlainDate/prototype/since/largestunit-default.js
new file mode 100644
index 00000000000..dbeb5699d69
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/largestunit-default.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Default value for largestUnit option is days
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const feb20 = Temporal.PlainDate.from("2020-02-01");
+const feb21 = Temporal.PlainDate.from("2021-02-01");
+TemporalHelpers.assertDuration(feb21.since(feb20), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, 0, "no options");
+TemporalHelpers.assertDuration(feb21.since(feb20, undefined), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, 0, "undefined options");
+TemporalHelpers.assertDuration(feb21.since(feb20, {}), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, 0, "no largestUnit");
+TemporalHelpers.assertDuration(feb21.since(feb20, { largestUnit: undefined }), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, 0, "undefined largestUnit");
+TemporalHelpers.assertDuration(feb21.since(feb20, { largestUnit: "days" }), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, 0, "days");
+TemporalHelpers.assertDuration(feb21.since(feb20, { largestUnit: "auto" }), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, 0, "auto");
+TemporalHelpers.assertDuration(feb21.since(feb20, () => {}), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, 0, "no largestUnit (function)");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/largestunit-higher-units.js b/test/built-ins/Temporal/PlainDate/prototype/since/largestunit-higher-units.js
new file mode 100644
index 00000000000..c1d8068ee4e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/largestunit-higher-units.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Tests calculations with higher largestUnit than the default of 'days'
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(1969, 7, 24);
+const later = Temporal.PlainDate.from({ year: 2019, month: 7, day: 24 });
+const duration = later.since(date, { largestUnit: "years" });
+TemporalHelpers.assertDuration(duration, /* years = */ 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, "crossing epoch");
+
+const feb20 = Temporal.PlainDate.from("2020-02-01");
+const feb21 = Temporal.PlainDate.from("2021-02-01");
+TemporalHelpers.assertDuration(feb21.since(feb20, { largestUnit: "years" }), /* years = */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "start of February, years");
+TemporalHelpers.assertDuration(feb21.since(feb20, { largestUnit: "months" }), 0, /* months = */ 12, 0, 0, 0, 0, 0, 0, 0, 0, "start of February, months");
+TemporalHelpers.assertDuration(feb21.since(feb20, { largestUnit: "weeks" }), 0, 0, /* weeks = */ 52, /* days = */ 2, 0, 0, 0, 0, 0, 0, 0, "start of February, weeks");
+
+const lastFeb20 = Temporal.PlainDate.from("2020-02-29");
+const lastFeb21 = Temporal.PlainDate.from("2021-02-28");
+TemporalHelpers.assertDuration(lastFeb21.since(lastFeb20, { largestUnit: "years" }), 0, /* months = */ 11, 0, /* days = */ 28, 0, 0, 0, 0, 0, 0, 0, "end of February, years");
+TemporalHelpers.assertDuration(lastFeb21.since(lastFeb20, { largestUnit: "months" }), 0, /* months = */ 11, 0, /* days = */ 28, 0, 0, 0, 0, 0, 0, "end of February, months");
+TemporalHelpers.assertDuration(lastFeb21.since(lastFeb20, { largestUnit: "weeks" }), 0, 0, /* weeks = */ 52, /* days = */ 1, 0, 0, 0, 0, 0, 0, "end of February, weeks");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/largestunit-invalid-string.js b/test/built-ins/Temporal/PlainDate/prototype/since/largestunit-invalid-string.js
new file mode 100644
index 00000000000..ffaa7cb0d8d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/largestunit-invalid-string.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+const values = ["hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds", "other string"];
+for (const largestUnit of values) {
+ assert.throws(RangeError, () => later.since(earlier, { largestUnit }));
+}
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/largestunit-plurals-accepted.js b/test/built-ins/Temporal/PlainDate/prototype/since/largestunit-plurals-accepted.js
new file mode 100644
index 00000000000..41dd5e5afd0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/largestunit-plurals-accepted.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 12);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => later.since(earlier, { largestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/largestunit-smallestunit-mismatch.js b/test/built-ins/Temporal/PlainDate/prototype/since/largestunit-smallestunit-mismatch.js
new file mode 100644
index 00000000000..8b1894c3fdc
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/largestunit-smallestunit-mismatch.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: RangeError thrown when smallestUnit is larger than largestUnit
+features: [Temporal]
+---*/
+
+const earlier = Temporal.PlainDate.from("2019-01-08");
+const later = Temporal.PlainDate.from("2021-09-07");
+const units = ["years", "months", "weeks", "days"];
+for (let largestIdx = 1; largestIdx < units.length; largestIdx++) {
+ for (let smallestIdx = 0; smallestIdx < largestIdx; smallestIdx++) {
+ const largestUnit = units[largestIdx];
+ const smallestUnit = units[smallestIdx];
+ assert.throws(RangeError, () => later.since(earlier, { largestUnit, smallestUnit }));
+ }
+}
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/largestunit-wrong-type.js b/test/built-ins/Temporal/PlainDate/prototype/since/largestunit-wrong-type.js
new file mode 100644
index 00000000000..2d84b200b3e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/largestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "year",
+ (largestUnit) => later.since(earlier, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/length.js b/test/built-ins/Temporal/PlainDate/prototype/since/length.js
new file mode 100644
index 00000000000..6938a510533
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Temporal.PlainDate.prototype.since.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.since, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/name.js b/test/built-ins/Temporal/PlainDate/prototype/since/name.js
new file mode 100644
index 00000000000..587c0ef8472
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Temporal.PlainDate.prototype.since.name is "since".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.since, "name", {
+ value: "since",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/not-a-constructor.js b/test/built-ins/Temporal/PlainDate/prototype/since/not-a-constructor.js
new file mode 100644
index 00000000000..d6743dd6a5f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: >
+ Temporal.PlainDate.prototype.since does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.since();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.since), false,
+ "isConstructor(Temporal.PlainDate.prototype.since)");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/options-invalid.js b/test/built-ins/Temporal/PlainDate/prototype/since/options-invalid.js
new file mode 100644
index 00000000000..58d22815ab2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/options-invalid.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: TypeError thrown when a primitive is passed as the options argument
+features: [Temporal]
+---*/
+
+const feb20 = Temporal.PlainDate.from("2020-02-01");
+const feb21 = Temporal.PlainDate.from("2021-02-01");
+const values = [null, true, "hello", Symbol("foo"), 1, 1n];
+for (const badOptions of values) {
+ assert.throws(TypeError, () => feb21.since(feb20, badOptions));
+}
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/since/prop-desc.js
new file mode 100644
index 00000000000..3989b670e80
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: The "since" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.since,
+ "function",
+ "`typeof PlainDate.prototype.since` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "since", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-nan.js b/test/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-nan.js
new file mode 100644
index 00000000000..bddc28e6dd8
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-nan.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindate.prototype.since step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: NaN }));
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-non-integer.js b/test/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-non-integer.js
new file mode 100644
index 00000000000..e1a4608236a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-non-integer.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Rounding for roundingIncrement option
+info: |
+ sec-temporal-totemporalroundingincrement step 7:
+ 7. Set _increment_ to floor(ℝ(_increment_)).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+const result = later.since(earlier, { roundingIncrement: 2.5 });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, "roundingIncrement 2.5 floors to 2");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-out-of-range.js b/test/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-out-of-range.js
new file mode 100644
index 00000000000..5fef7e40de2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-out-of-range.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ sec-temporal-totemporalroundingincrement step 6:
+ 6. If _increment_ < 1 or _increment_ > _maximum_, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: Infinity }));
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-undefined.js b/test/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-undefined.js
new file mode 100644
index 00000000000..50bdfeb4832
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-undefined.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindate.prototype.since step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+
+const explicit = later.since(earlier, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, "default roundingIncrement is 1");
+
+// See options-undefined.js for {}
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-wrong-type.js b/test/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-wrong-type.js
new file mode 100644
index 00000000000..852567987dc
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/roundingincrement-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindate.prototype.since step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => later.since(earlier, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/roundingmode-invalid-string.js b/test/built-ins/Temporal/PlainDate/prototype/since/roundingmode-invalid-string.js
new file mode 100644
index 00000000000..cf9d9e20c7a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/roundingmode-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "microsecond", roundingMode: "other string" }));
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/roundingmode-undefined.js b/test/built-ins/Temporal/PlainDate/prototype/since/roundingmode-undefined.js
new file mode 100644
index 00000000000..0d4a088b896
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/roundingmode-undefined.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 1, 1);
+
+const later1 = new Temporal.PlainDate(2005, 2, 20);
+const explicit1 = later1.since(earlier, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit1 = later1.since(earlier, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+
+const later2 = new Temporal.PlainDate(2005, 12, 15);
+const explicit2 = later2.since(earlier, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit2 = later2.since(earlier, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/roundingmode-wrong-type.js b/test/built-ins/Temporal/PlainDate/prototype/since/roundingmode-wrong-type.js
new file mode 100644
index 00000000000..9e872fac3ac
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/roundingmode-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => later.since(earlier, { smallestUnit: "year", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/smallestunit-invalid-string.js b/test/built-ins/Temporal/PlainDate/prototype/since/smallestunit-invalid-string.js
new file mode 100644
index 00000000000..6f8abc39397
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/smallestunit-invalid-string.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+const values = ["era", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds", "other string"];
+for (const smallestUnit of values) {
+ assert.throws(RangeError, () => later.since(earlier, { smallestUnit }));
+}
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/smallestunit-plurals-accepted.js b/test/built-ins/Temporal/PlainDate/prototype/since/smallestunit-plurals-accepted.js
new file mode 100644
index 00000000000..1bed0f656f8
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/smallestunit-plurals-accepted.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 12);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => later.since(earlier, { smallestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/smallestunit-undefined.js b/test/built-ins/Temporal/PlainDate/prototype/since/smallestunit-undefined.js
new file mode 100644
index 00000000000..3a37e058533
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/smallestunit-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+
+const explicit = later.since(earlier, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, "default smallestUnit is day");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, "default smallestUnit is day");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/smallestunit-wrong-type.js b/test/built-ins/Temporal/PlainDate/prototype/since/smallestunit-wrong-type.js
new file mode 100644
index 00000000000..8f41f5e76bb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/smallestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.since
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "year",
+ (smallestUnit) => later.since(earlier, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/subtract/argument-not-object.js b/test/built-ins/Temporal/PlainDate/prototype/subtract/argument-not-object.js
new file mode 100644
index 00000000000..540356eda7b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/subtract/argument-not-object.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Passing a primitive other than string to subtract() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+assert.throws(RangeError, () => instance.subtract(undefined), "undefined");
+assert.throws(RangeError, () => instance.subtract(null), "null");
+assert.throws(RangeError, () => instance.subtract(true), "boolean");
+assert.throws(RangeError, () => instance.subtract(""), "empty string");
+assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol");
+assert.throws(RangeError, () => instance.subtract(7), "number");
+assert.throws(RangeError, () => instance.subtract(7n), "bigint");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/subtract/argument-string-negative-fractional-units.js b/test/built-ins/Temporal/PlainDate/prototype/subtract/argument-string-negative-fractional-units.js
new file mode 100644
index 00000000000..fb657c460e4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/subtract/argument-string-negative-fractional-units.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+const resultHours = instance.subtract("-PT24.567890123H");
+TemporalHelpers.assertPlainDate(resultHours, 2000, 5, "M05", 3, "negative fractional hours");
+
+const resultMinutes = instance.subtract("-PT1440.567890123M");
+TemporalHelpers.assertPlainDate(resultMinutes, 2000, 5, "M05", 3, "negative fractional minutes");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/subtract/argument-string.js b/test/built-ins/Temporal/PlainDate/prototype/subtract/argument-string.js
new file mode 100644
index 00000000000..a78a8ea9565
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/subtract/argument-string.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+const result = instance.subtract("P3D");
+TemporalHelpers.assertPlainDate(result, 2000, 4, "M04", 29);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units.js b/test/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units.js
new file mode 100644
index 00000000000..aa49cb1cd6e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units.js
@@ -0,0 +1,53 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.dateadd
+description: Durations with units smaller than days are balanced before adding, in the calendar
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+
+class DateAddCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateAdd(date, duration, options) {
+ actual.push(duration);
+ return super.dateAdd(date, duration, options);
+ }
+}
+
+const calendar = new DateAddCalendar();
+const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+const duration = new Temporal.Duration(0, 0, 0, 1, 24, 1440, 86400, 86400_000, 86400_000_000, 86400_000_000_000);
+
+const result = date.subtract(duration);
+TemporalHelpers.assertPlainDate(result, 2000, 4, "M04", 25, "units smaller than days are balanced");
+
+assert.sameValue(actual.length, 1, "calendar.dateAdd called exactly once");
+TemporalHelpers.assertDuration(actual[0], 0, 0, 0, -1, -24, -1440, -86400, -86400_000, -86400_000_000, -86400_000_000_000, "the duration is negated but not balanced before passing to the calendar");
+assert.notSameValue(actual[0], duration, "the duration is not passed directly to the calendar");
+
+const resultString = date.subtract("P1DT24H1440M86400S");
+TemporalHelpers.assertPlainDate(resultString, 2000, 4, "M04", 28, "units smaller than days are balanced");
+
+assert.sameValue(actual.length, 2, "calendar.dateAdd called exactly once");
+TemporalHelpers.assertDuration(actual[1], 0, 0, 0, -1, -24, -1440, -86400, 0, 0, 0, "the duration is negated but not balanced before passing to the calendar");
+
+const resultPropBag = date.subtract({ days: 1, hours: 24, minutes: 1440, seconds: 86400, milliseconds: 86400_000, microseconds: 86400_000_000, nanoseconds: 86400_000_000_000 });
+TemporalHelpers.assertPlainDate(resultPropBag, 2000, 4, "M04", 25, "units smaller than days are balanced");
+
+assert.sameValue(actual.length, 3, "calendar.dateAdd called exactly once");
+TemporalHelpers.assertDuration(actual[2], 0, 0, 0, -1, -24, -1440, -86400, -86400_000, -86400_000_000, -86400_000_000_000, "the duration is not balanced before passing to the calendar");
+
+const negativeDuration = new Temporal.Duration(0, 0, 0, -1, -24, -1440, -86400, -86400_000, -86400_000_000, -86400_000_000_000);
+const resultNegative = date.subtract(negativeDuration);
+TemporalHelpers.assertPlainDate(resultNegative, 2000, 5, "M05", 9, "units smaller than days are balanced");
+
+assert.sameValue(actual.length, 4, "calendar.dateAdd called exactly once");
+TemporalHelpers.assertDuration(actual[3], 0, 0, 0, 1, 24, 1440, 86400, 86400_000, 86400_000_000, 86400_000_000_000, "the duration is negated but not balanced before passing to the calendar");
+assert.notSameValue(actual[3], negativeDuration, "the duration is not passed directly to the calendar");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/subtract/builtin.js b/test/built-ins/Temporal/PlainDate/prototype/subtract/builtin.js
new file mode 100644
index 00000000000..e7e659066cd
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/subtract/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: >
+ Tests that Temporal.PlainDate.prototype.subtract
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.subtract),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.subtract),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.subtract),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.subtract.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/subtract/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDate/prototype/subtract/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..d66784adeca
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/subtract/infinity-throws-rangeerror.js
@@ -0,0 +1,35 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDate.prototype.subtract throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.plaindate.prototype.subtract
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/subtract/length.js b/test/built-ins/Temporal/PlainDate/prototype/subtract/length.js
new file mode 100644
index 00000000000..6f4de6acb49
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/subtract/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Temporal.PlainDate.prototype.subtract.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.subtract, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/subtract/name.js b/test/built-ins/Temporal/PlainDate/prototype/subtract/name.js
new file mode 100644
index 00000000000..82cd31c7b66
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/subtract/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Temporal.PlainDate.prototype.subtract.name is "subtract".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.subtract, "name", {
+ value: "subtract",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/subtract/negative-infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDate/prototype/subtract/negative-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..f3858b47c12
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/subtract/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,35 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDate.prototype.subtract throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.plaindate.prototype.subtract
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: -Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/subtract/non-integer-throws-rangeerror.js b/test/built-ins/Temporal/PlainDate/prototype/subtract/non-integer-throws-rangeerror.js
new file mode 100644
index 00000000000..67828b15f87
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/subtract/non-integer-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.subtract({ [field]: -1.5 }));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/subtract/not-a-constructor.js b/test/built-ins/Temporal/PlainDate/prototype/subtract/not-a-constructor.js
new file mode 100644
index 00000000000..5642fc96d00
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/subtract/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: >
+ Temporal.PlainDate.prototype.subtract does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.subtract();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.subtract), false,
+ "isConstructor(Temporal.PlainDate.prototype.subtract)");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/subtract/options-invalid.js b/test/built-ins/Temporal/PlainDate/prototype/subtract/options-invalid.js
new file mode 100644
index 00000000000..5d4f1c671a9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/subtract/options-invalid.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: TypeError thrown when a primitive is passed as the options argument
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 3, 31);
+const duration = { months: 1 };
+
+const values = [null, true, "hello", Symbol("foo"), 1, 1n];
+for (const badOptions of values) {
+ assert.throws(TypeError, () => plainDate.subtract(duration, badOptions));
+}
diff --git a/test/built-ins/Temporal/PlainDate/prototype/subtract/options-undefined.js b/test/built-ins/Temporal/PlainDate/prototype/subtract/options-undefined.js
new file mode 100644
index 00000000000..baf542fddd3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/subtract/options-undefined.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 3, 31);
+const duration = { months: 1 };
+
+const explicit = date.subtract(duration, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = date.subtract(duration);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/subtract/order-of-operations.js b/test/built-ins/Temporal/PlainDate/prototype/subtract/order-of-operations.js
new file mode 100644
index 00000000000..a59ae9c670c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/subtract/order-of-operations.js
@@ -0,0 +1,74 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Properties on an object passed to subtract() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const expected = [
+ "get days",
+ "get days.valueOf",
+ "call days.valueOf",
+ "get hours",
+ "get hours.valueOf",
+ "call hours.valueOf",
+ "get microseconds",
+ "get microseconds.valueOf",
+ "call microseconds.valueOf",
+ "get milliseconds",
+ "get milliseconds.valueOf",
+ "call milliseconds.valueOf",
+ "get minutes",
+ "get minutes.valueOf",
+ "call minutes.valueOf",
+ "get months",
+ "get months.valueOf",
+ "call months.valueOf",
+ "get nanoseconds",
+ "get nanoseconds.valueOf",
+ "call nanoseconds.valueOf",
+ "get seconds",
+ "get seconds.valueOf",
+ "call seconds.valueOf",
+ "get weeks",
+ "get weeks.valueOf",
+ "call weeks.valueOf",
+ "get years",
+ "get years.valueOf",
+ "call years.valueOf",
+];
+const actual = [];
+const fields = {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = instance.subtract(argument);
+TemporalHelpers.assertPlainDate(result, 1999, 3, "M03", 25);
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/subtract/overflow-invalid-string.js b/test/built-ins/Temporal/PlainDate/prototype/subtract/overflow-invalid-string.js
new file mode 100644
index 00000000000..842e02756ce
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/subtract/overflow-invalid-string.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.subtract step 7:
+ 7. Return ? CalendarDateAdd(_temporalDate_.[[Calendar]], _temporalDate_, _negatedDuration_, _options_).
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const duration = new Temporal.Duration(3, 3, 0, 3);
+assert.throws(RangeError, () => date.subtract(duration, { overflow: "other string" }));
diff --git a/test/built-ins/Temporal/PlainDate/prototype/subtract/overflow-undefined.js b/test/built-ins/Temporal/PlainDate/prototype/subtract/overflow-undefined.js
new file mode 100644
index 00000000000..efd011b8245
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/subtract/overflow-undefined.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.subtract step 7:
+ 7. Return ? CalendarDateAdd(_temporalDate_.[[Calendar]], _temporalDate_, _negatedDuration_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 31);
+const duration = new Temporal.Duration(3, 1);
+
+const explicit = date.subtract(duration, { overflow: undefined });
+TemporalHelpers.assertPlainDate(explicit, 1997, 4, "M04", 30, "default overflow is constrain");
+const implicit = date.subtract(duration, {});
+TemporalHelpers.assertPlainDate(implicit, 1997, 4, "M04", 30, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/subtract/overflow-wrong-type.js b/test/built-ins/Temporal/PlainDate/prototype/subtract/overflow-wrong-type.js
new file mode 100644
index 00000000000..7b2ad364337
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/subtract/overflow-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.subtract step 7:
+ 7. Return ? CalendarDateAdd(_temporalDate_.[[Calendar]], _temporalDate_, _negatedDuration_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const duration = new Temporal.Duration(3, 3, 0, 3);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => date.subtract(duration, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDate(result, 1997, 1, "M01", 30, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/subtract/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/subtract/prop-desc.js
new file mode 100644
index 00000000000..aa8522fd166
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/subtract/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: The "subtract" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.subtract,
+ "function",
+ "`typeof PlainDate.prototype.subtract` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "subtract", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/subtract/subclassing-ignored.js b/test/built-ins/Temporal/PlainDate/prototype/subtract/subclassing-ignored.js
new file mode 100644
index 00000000000..853b98f35dd
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/subtract/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Objects of a subclass are never created as return values for subtract()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDate,
+ [2000, 5, 2],
+ "subtract",
+ [{ days: 1 }],
+ (result) => TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 1),
+);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toJSON/builtin.js b/test/built-ins/Temporal/PlainDate/prototype/toJSON/builtin.js
new file mode 100644
index 00000000000..574d4928332
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toJSON/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tojson
+description: >
+ Tests that Temporal.PlainDate.prototype.toJSON
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toJSON/length.js b/test/built-ins/Temporal/PlainDate/prototype/toJSON/length.js
new file mode 100644
index 00000000000..a71a8032b95
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toJSON/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tojson
+description: Temporal.PlainDate.prototype.toJSON.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toJSON/name.js b/test/built-ins/Temporal/PlainDate/prototype/toJSON/name.js
new file mode 100644
index 00000000000..64f6b9ad5e7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toJSON/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tojson
+description: Temporal.PlainDate.prototype.toJSON.name is "toJSON".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toJSON/not-a-constructor.js b/test/built-ins/Temporal/PlainDate/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 00000000000..afed9683c3d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toJSON/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tojson
+description: >
+ Temporal.PlainDate.prototype.toJSON does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.toJSON), false,
+ "isConstructor(Temporal.PlainDate.prototype.toJSON)");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toJSON/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/toJSON/prop-desc.js
new file mode 100644
index 00000000000..a275732d87d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toJSON/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tojson
+description: The "toJSON" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.toJSON,
+ "function",
+ "`typeof PlainDate.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/builtin.js b/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/builtin.js
new file mode 100644
index 00000000000..eede0bfd2ba
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tolocalestring
+description: >
+ Tests that Temporal.PlainDate.prototype.toLocaleString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.toLocaleString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.toLocaleString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.toLocaleString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.toLocaleString.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/length.js b/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/length.js
new file mode 100644
index 00000000000..9d6dcb5d1a5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tolocalestring
+description: Temporal.PlainDate.prototype.toLocaleString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toLocaleString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/locales-undefined.js b/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/locales-undefined.js
new file mode 100644
index 00000000000..f2928e224ea
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/locales-undefined.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tolocalestring
+description: Omitting the locales argument defaults to the DateTimeFormat default
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const defaultFormatter = new Intl.DateTimeFormat([], Object.create(null));
+const expected = defaultFormatter.format(date);
+
+const actualExplicit = date.toLocaleString(undefined);
+assert.sameValue(actualExplicit, expected, "default locale is determined by Intl.DateTimeFormat");
+
+const actualImplicit = date.toLocaleString();
+assert.sameValue(actualImplicit, expected, "default locale is determined by Intl.DateTimeFormat");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/name.js b/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/name.js
new file mode 100644
index 00000000000..a15b0dbd5ed
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tolocalestring
+description: Temporal.PlainDate.prototype.toLocaleString.name is "toLocaleString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toLocaleString, "name", {
+ value: "toLocaleString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/not-a-constructor.js b/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/not-a-constructor.js
new file mode 100644
index 00000000000..5f3dac64ea1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tolocalestring
+description: >
+ Temporal.PlainDate.prototype.toLocaleString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.toLocaleString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.toLocaleString), false,
+ "isConstructor(Temporal.PlainDate.prototype.toLocaleString)");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/options-conflict.js b/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/options-conflict.js
new file mode 100644
index 00000000000..3deb4c2b244
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/options-conflict.js
@@ -0,0 +1,37 @@
+// Copyright (C) 2021 Kate Miháliková. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sup-temporal.plaindate.prototype.tolocalestring
+description: >
+ Conflicting properties of dateStyle must be rejected with a TypeError for the options argument
+info: |
+ Using sec-temporal-getdatetimeformatpattern:
+ GetDateTimeFormatPattern ( dateStyle, timeStyle, matcher, opt, dataLocaleData, hc )
+
+ 1. If dateStyle is not undefined or timeStyle is not undefined, then
+ a. For each row in Table 7, except the header row, do
+ i. Let prop be the name given in the Property column of the row.
+ ii. Let p be opt.[[]].
+ iii. If p is not undefined, then
+ 1. Throw a TypeError exception.
+features: [Temporal]
+---*/
+
+// Table 14 - Supported fields + example value for each field
+const conflictingOptions = [
+ [ "weekday", "short" ],
+ [ "era", "short" ],
+ [ "year", "numeric" ],
+ [ "month", "numeric" ],
+ [ "day", "numeric" ],
+];
+const date = new Temporal.PlainDate(2000, 5, 2);
+
+assert.sameValue(typeof date.toLocaleString("en", { dateStyle: "short" }), "string");
+
+for (const [ option, value ] of conflictingOptions) {
+ assert.throws(TypeError, function() {
+ date.toLocaleString("en", { [option]: value, dateStyle: "short" });
+ }, `date.toLocaleString("en", { ${option}: "${value}", dateStyle: "short" }) throws TypeError`);
+}
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/options-undefined.js b/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/options-undefined.js
new file mode 100644
index 00000000000..b95280c8826
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/options-undefined.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tolocalestring
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const defaultFormatter = new Intl.DateTimeFormat('en', Object.create(null));
+const expected = defaultFormatter.format(date);
+
+const actualExplicit = date.toLocaleString('en', undefined);
+assert.sameValue(actualExplicit, expected, "default locale is determined by Intl.DateTimeFormat");
+
+const actualImplicit = date.toLocaleString('en');
+assert.sameValue(actualImplicit, expected, "default locale is determined by Intl.DateTimeFormat");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/prop-desc.js
new file mode 100644
index 00000000000..e9376034a32
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toLocaleString/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tolocalestring
+description: The "toLocaleString" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.toLocaleString,
+ "function",
+ "`typeof PlainDate.prototype.toLocaleString` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "toLocaleString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-balance-negative-time-units.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 00000000000..fa98f2c05df
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,44 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: Negative time fields are balanced upwards if the argument is given as ZonedDateTime
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-totemporaltime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Set _plainDateTime_ to ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaindate.prototype.toplaindatetime step 4:
+ 4. Set _temporalTime_ to ? ToTemporalTime(_temporalTime_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// exact time's epoch parts
+const tz = new Temporal.TimeZone("-00:00:00.000000002");
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const pdt = date.toPlainDateTime(datetime);
+
+TemporalHelpers.assertPlainDateTime(pdt, 2000, 5, "M05", 2, 1, 1, 1, 1, 0, 999);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-negative-epochnanoseconds.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 00000000000..ec19024fc1b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const result = instance.toPlainDateTime(datetime);
+TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 16, 50, 35, 0, 0, 1);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..ad8dc11155a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.toPlainDateTime(datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..3d522fe48d7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.toPlainDateTime(datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..3af8fa988f6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => date.toPlainDateTime(datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/basic.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/basic.js
new file mode 100644
index 00000000000..f9bdc521975
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/basic.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: Basic tests for toPlainDateTime().
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+
+const string = date.toPlainDateTime("11:30:23");
+TemporalHelpers.assertPlainDateTime(string, 2000, 5, "M05", 2, 11, 30, 23, 0, 0, 0, "string");
+
+const optionBag = date.toPlainDateTime({ hour: 11, minute: 30, second: 23 });
+TemporalHelpers.assertPlainDateTime(optionBag, 2000, 5, "M05", 2, 11, 30, 23, 0, 0, 0, "option bag");
+
+const plainTime = date.toPlainDateTime(Temporal.PlainTime.from("11:30:23"));
+TemporalHelpers.assertPlainDateTime(plainTime, 2000, 5, "M05", 2, 11, 30, 23, 0, 0, 0, "PlainTime");
+
+const plainDateTime = date.toPlainDateTime(Temporal.PlainDateTime.from("1999-07-14T11:30:23"));
+TemporalHelpers.assertPlainDateTime(plainDateTime, 2000, 5, "M05", 2, 11, 30, 23, 0, 0, 0, "PlainTime");
+
+const zonedDateTime = date.toPlainDateTime(Temporal.ZonedDateTime.from("1999-07-14T11:30:23Z[UTC]"));
+TemporalHelpers.assertPlainDateTime(zonedDateTime, 2000, 5, "M05", 2, 11, 30, 23, 0, 0, 0, "PlainTime");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/builtin.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/builtin.js
new file mode 100644
index 00000000000..0af44ec5478
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: >
+ Tests that Temporal.PlainDate.prototype.toPlainDateTime
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.toPlainDateTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.toPlainDateTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.toPlainDateTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.toPlainDateTime.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/calendar-temporal-object.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/calendar-temporal-object.js
new file mode 100644
index 00000000000..5f58688a575
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/calendar-temporal-object.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindate.prototype.toplaindatetime step 4:
+ 4. Set _temporalTime_ to ? ToTemporalTime(_temporalTime_).
+ sec-temporal-totemporaltime step 3.d:
+ d. If _calendar_ is not *undefined*, then
+ i. Set _calendar_ to ? ToTemporalCalendar(_calendar_).
+ ii. If ? ToString(_calendar_) is not *"iso8601"*, then
+ 1. Throw a *RangeError* exception.
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ assert.throws(RangeError, () => date.toPlainDateTime({ hour: 12, minute: 30, calendar: temporalObject }));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/length.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/length.js
new file mode 100644
index 00000000000..c44975fd5c9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: Temporal.PlainDate.prototype.toPlainDateTime.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toPlainDateTime, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/name.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/name.js
new file mode 100644
index 00000000000..c39b036ef6c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: Temporal.PlainDate.prototype.toPlainDateTime.name is "toPlainDateTime".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toPlainDateTime, "name", {
+ value: "toPlainDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/not-a-constructor.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/not-a-constructor.js
new file mode 100644
index 00000000000..375a942a451
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: >
+ Temporal.PlainDate.prototype.toPlainDateTime does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.toPlainDateTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.toPlainDateTime), false,
+ "isConstructor(Temporal.PlainDate.prototype.toPlainDateTime)");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/prop-desc.js
new file mode 100644
index 00000000000..e6542f784d8
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: The "toPlainDateTime" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.toPlainDateTime,
+ "function",
+ "`typeof PlainDate.prototype.toPlainDateTime` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "toPlainDateTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/time-invalid.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/time-invalid.js
new file mode 100644
index 00000000000..9f354b75fb2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/time-invalid.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: TypeError thrown if an invalid property bag passed
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 5, 2);
+assert.throws(TypeError, () => plainDate.toPlainDateTime({}), "empty object");
+assert.throws(TypeError, () => plainDate.toPlainDateTime({ minutes: 30 }), "plural property");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/time-undefined.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/time-undefined.js
new file mode 100644
index 00000000000..ec2032e80a0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/time-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplaindatetime
+description: The time is assumed to be midnight if not given
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+
+const explicit = date.toPlainDateTime(undefined);
+TemporalHelpers.assertPlainDateTime(explicit, 2000, 5, "M05", 2, 0, 0, 0, 0, 0, 0, "default time is midnight - explicit");
+
+const implicit = date.toPlainDateTime();
+TemporalHelpers.assertPlainDateTime(implicit, 2000, 5, "M05", 2, 0, 0, 0, 0, 0, 0, "default time is midnight - implicit");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin.js
new file mode 100644
index 00000000000..e03db7cead1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: >
+ Tests that Temporal.PlainDate.prototype.toPlainMonthDay
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.toPlainMonthDay),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.toPlainMonthDay),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.toPlainMonthDay),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.toPlainMonthDay.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-arguments.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-arguments.js
new file mode 100644
index 00000000000..b8dda84d662
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-arguments.js
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: Correct options value is passed to calendar method
+info: |
+ MonthDayFromFields ( calendar, fields [ , options ] )
+
+ 3. If options is not present, then
+ a. Set options to undefined.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthDayFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.sameValue(args[1], undefined, "args[1]");
+ return super.monthDayFromFields(...args);
+ }
+}
+const plainDate = new Temporal.PlainDate(2000, 5, 2, new CustomCalendar());
+const result = plainDate.toPlainMonthDay();
+TemporalHelpers.assertPlainMonthDay(result, "M05", 2);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-fields-iterable.js
new file mode 100644
index 00000000000..96e39b8b35b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/calendar-fields-iterable.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.prototype.toplainmonthday step 4:
+ 4. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"monthCode"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "monthCode",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+date.toPlainMonthDay();
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/length.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/length.js
new file mode 100644
index 00000000000..28250731569
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: Temporal.PlainDate.prototype.toPlainMonthDay.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toPlainMonthDay, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/name.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/name.js
new file mode 100644
index 00000000000..7bda93658a6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: Temporal.PlainDate.prototype.toPlainMonthDay.name is "toPlainMonthDay".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toPlainMonthDay, "name", {
+ value: "toPlainMonthDay",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/not-a-constructor.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/not-a-constructor.js
new file mode 100644
index 00000000000..e52949d31cd
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: >
+ Temporal.PlainDate.prototype.toPlainMonthDay does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.toPlainMonthDay();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.toPlainMonthDay), false,
+ "isConstructor(Temporal.PlainDate.prototype.toPlainMonthDay)");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/prop-desc.js
new file mode 100644
index 00000000000..10fe1762083
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainMonthDay/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainmonthday
+description: The "toPlainMonthDay" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.toPlainMonthDay,
+ "function",
+ "`typeof PlainDate.prototype.toPlainMonthDay` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "toPlainMonthDay", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin.js
new file mode 100644
index 00000000000..ff975caeca5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: >
+ Tests that Temporal.PlainDate.prototype.toPlainYearMonth
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.toPlainYearMonth),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.toPlainYearMonth),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.toPlainYearMonth),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.toPlainYearMonth.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-arguments.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-arguments.js
new file mode 100644
index 00000000000..5dd51a40c1b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-arguments.js
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: Correct options value is passed to calendar method
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 3. If options is not present, then
+ a. Set options to undefined.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.sameValue(args[1], undefined, "args[1]");
+ return super.yearMonthFromFields(...args);
+ }
+}
+const plainDate = new Temporal.PlainDate(2000, 5, 2, new CustomCalendar());
+const result = plainDate.toPlainYearMonth();
+TemporalHelpers.assertPlainYearMonth(result, 2000, 5, "M05");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-fields-iterable.js
new file mode 100644
index 00000000000..7203335d86f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/calendar-fields-iterable.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.prototype.toplainyearmonth step 4:
+ 4. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+date.toPlainYearMonth();
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/length.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/length.js
new file mode 100644
index 00000000000..9e1c0d5a802
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: Temporal.PlainDate.prototype.toPlainYearMonth.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toPlainYearMonth, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/name.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/name.js
new file mode 100644
index 00000000000..dddc6f0f912
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: Temporal.PlainDate.prototype.toPlainYearMonth.name is "toPlainYearMonth".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toPlainYearMonth, "name", {
+ value: "toPlainYearMonth",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/not-a-constructor.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/not-a-constructor.js
new file mode 100644
index 00000000000..31cc670c740
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: >
+ Temporal.PlainDate.prototype.toPlainYearMonth does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.toPlainYearMonth();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.toPlainYearMonth), false,
+ "isConstructor(Temporal.PlainDate.prototype.toPlainYearMonth)");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/prop-desc.js
new file mode 100644
index 00000000000..c7b42f80a68
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toPlainYearMonth/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.toplainyearmonth
+description: The "toPlainYearMonth" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.toPlainYearMonth,
+ "function",
+ "`typeof PlainDate.prototype.toPlainYearMonth` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "toPlainYearMonth", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toString/builtin.js b/test/built-ins/Temporal/PlainDate/prototype/toString/builtin.js
new file mode 100644
index 00000000000..7a9569f9479
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toString/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tostring
+description: >
+ Tests that Temporal.PlainDate.prototype.toString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toString/calendarname-invalid-string.js b/test/built-ins/Temporal/PlainDate/prototype/toString/calendarname-invalid-string.js
new file mode 100644
index 00000000000..bca22e7be7c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toString/calendarname-invalid-string.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.protoype.tostring
+description: RangeError thrown when calendarName option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « String », « *"auto"*, *"always"*, *"never"* », *"auto"*).
+ sec-temporal.plaindate.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const values = ["ALWAYS", "sometimes", "other string"];
+
+for (const calendarName of values) {
+ assert.throws(RangeError, () => date.toString({ calendarName }));
+}
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toString/calendarname-undefined.js b/test/built-ins/Temporal/PlainDate/prototype/toString/calendarname-undefined.js
new file mode 100644
index 00000000000..0d5e93e787a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toString/calendarname-undefined.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.protoype.tostring
+description: Fallback value for calendarName option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « String », « *"auto"*, *"always"*, *"never"* », *"auto"*).
+ sec-temporal.plaindate.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const calendar = {
+ toString() { return "custom"; }
+};
+const date1 = new Temporal.PlainDate(2000, 5, 2);
+const date2 = new Temporal.PlainDate(2000, 5, 2, calendar);
+
+[
+ [date1, "2000-05-02"],
+ [date2, "2000-05-02[u-ca=custom]"],
+].forEach(([date, expected]) => {
+ const explicit = date.toString({ calendarName: undefined });
+ assert.sameValue(explicit, expected, "default calendarName option is auto");
+
+ const implicit = date.toString({});
+ assert.sameValue(implicit, expected, "default calendarName option is auto");
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toString/calendarname-wrong-type.js b/test/built-ins/Temporal/PlainDate/prototype/toString/calendarname-wrong-type.js
new file mode 100644
index 00000000000..2a621ea31f5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toString/calendarname-wrong-type.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.protoype.tostring
+description: Type conversions for calendarName option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « String », « *"auto"*, *"always"*, *"never"* », *"auto"*).
+ sec-temporal.plaindate.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = {
+ toString() { return "custom"; }
+};
+const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+
+TemporalHelpers.checkStringOptionWrongType("calendarName", "auto",
+ (calendarName) => date.toString({ calendarName }),
+ (result, descr) => assert.sameValue(result, "2000-05-02[u-ca=custom]", descr),
+);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toString/length.js b/test/built-ins/Temporal/PlainDate/prototype/toString/length.js
new file mode 100644
index 00000000000..5616608738d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toString/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tostring
+description: Temporal.PlainDate.prototype.toString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toString/name.js b/test/built-ins/Temporal/PlainDate/prototype/toString/name.js
new file mode 100644
index 00000000000..1a66abd66c5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toString/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tostring
+description: Temporal.PlainDate.prototype.toString.name is "toString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toString/not-a-constructor.js b/test/built-ins/Temporal/PlainDate/prototype/toString/not-a-constructor.js
new file mode 100644
index 00000000000..abe41cfe1b0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toString/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tostring
+description: >
+ Temporal.PlainDate.prototype.toString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.toString), false,
+ "isConstructor(Temporal.PlainDate.prototype.toString)");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toString/options-undefined.js b/test/built-ins/Temporal/PlainDate/prototype/toString/options-undefined.js
new file mode 100644
index 00000000000..389993ae727
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toString/options-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tostring
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const calendar = {
+ toString() { return "custom"; }
+};
+const date1 = new Temporal.PlainDate(2000, 5, 2);
+const date2 = new Temporal.PlainDate(2000, 5, 2, calendar);
+
+[
+ [date1, "2000-05-02"],
+ [date2, "2000-05-02[u-ca=custom]"],
+].forEach(([date, expected]) => {
+ const explicit = date.toString(undefined);
+ assert.sameValue(explicit, expected, "default calendarName option is auto");
+
+ const implicit = date.toString();
+ assert.sameValue(implicit, expected, "default calendarName option is auto");
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toString/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/toString/prop-desc.js
new file mode 100644
index 00000000000..5172e2b0057
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toString/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tostring
+description: The "toString" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.toString,
+ "function",
+ "`typeof PlainDate.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-zoneddatetime-negative-epochnanoseconds.js b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 00000000000..c1ab7725f8b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const result = instance.toZonedDateTime({ plainTime: datetime, timeZone: "UTC" });
+assert.sameValue(result.epochNanoseconds, 957286235_000_000_001n);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/basic.js b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/basic.js
new file mode 100644
index 00000000000..052aa88f668
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/basic.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Basic tests for toZonedDateTime().
+features: [Temporal]
+---*/
+
+const plainDate = Temporal.PlainDate.from("2020-01-01");
+const timeZone = Temporal.TimeZone.from("America/Los_Angeles");
+const plainTime = Temporal.PlainTime.from("12:00");
+
+let result = plainDate.toZonedDateTime({ timeZone, plainTime });
+assert.sameValue(result.toString(), "2020-01-01T12:00:00-08:00[America/Los_Angeles]", "objects passed");
+
+result = plainDate.toZonedDateTime(timeZone);
+assert.sameValue(result.toString(), "2020-01-01T00:00:00-08:00[America/Los_Angeles]", "time zone object argument");
+
+result = plainDate.toZonedDateTime("America/Los_Angeles");
+assert.sameValue(result.toString(), "2020-01-01T00:00:00-08:00[America/Los_Angeles]", "time zone string argument");
+
+result = plainDate.toZonedDateTime({ timeZone });
+assert.sameValue(result.toString(), "2020-01-01T00:00:00-08:00[America/Los_Angeles]", "time zone object property");
+
+result = plainDate.toZonedDateTime({ timeZone: "America/Los_Angeles", plainTime });
+assert.sameValue(result.toString(), "2020-01-01T12:00:00-08:00[America/Los_Angeles]", "time zone string property");
+
+result = plainDate.toZonedDateTime({ timeZone, plainTime: "12:00" });
+assert.sameValue(result.toString(), "2020-01-01T12:00:00-08:00[America/Los_Angeles]", "time string property");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/builtin.js b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/builtin.js
new file mode 100644
index 00000000000..a9b6de302ba
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: >
+ Tests that Temporal.PlainDate.prototype.toZonedDateTime
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.toZonedDateTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.toZonedDateTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.toZonedDateTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.toZonedDateTime.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/calendar-dateadd-called-with-options-undefined.js b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/calendar-dateadd-called-with-options-undefined.js
new file mode 100644
index 00000000000..04afc0acd6b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/calendar-dateadd-called-with-options-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: >
+ BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the
+ options value
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddUndefinedOptions();
+const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9);
+const instance = new Temporal.PlainDate(1970, 1, 1, calendar);
+instance.toZonedDateTime({ timeZone });
+assert.sameValue(calendar.dateAddCallCount, 1);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/calendar-temporal-object.js b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/calendar-temporal-object.js
new file mode 100644
index 00000000000..05324394219
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/calendar-temporal-object.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindate.prototype.tozoneddatetime step 6.a:
+ a. Set _temporalTime_ to ? ToTemporalTime(_temporalTime_).
+ sec-temporal-totemporaltime step 3.d:
+ d. If _calendar_ is not *undefined*, then
+ i. Set _calendar_ to ? ToTemporalCalendar(_calendar_).
+ ii. If ? ToString(_calendar_) is not *"iso8601"*, then
+ 1. Throw a *RangeError* exception.
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ assert.throws(RangeError, () => date.toZonedDateTime({ timeZone: "UTC", plainTime: { hour: 12, minute: 30, calendar: temporalObject } }));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/length.js b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/length.js
new file mode 100644
index 00000000000..7272e15a99e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Temporal.PlainDate.prototype.toZonedDateTime.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toZonedDateTime, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/name.js b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/name.js
new file mode 100644
index 00000000000..7a697df90a6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Temporal.PlainDate.prototype.toZonedDateTime.name is "toZonedDateTime".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.toZonedDateTime, "name", {
+ value: "toZonedDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/not-a-constructor.js b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/not-a-constructor.js
new file mode 100644
index 00000000000..d27ac5ac74e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: >
+ Temporal.PlainDate.prototype.toZonedDateTime does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.toZonedDateTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.toZonedDateTime), false,
+ "isConstructor(Temporal.PlainDate.prototype.toZonedDateTime)");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-balance-negative-time-units.js b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 00000000000..2b97bd1b5aa
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,45 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Negative time fields are balanced upwards in a ZonedDateTime given as plainTime
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-totemporaltime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Set _plainDateTime_ to ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaindate.prototype.tozoneddatetime step 6.a:
+ a. Set _temporalTime_ to ? ToTemporalTime(_temporalTime_).
+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 = new Temporal.TimeZone("-00:00:00.000000002");
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+const otherTimeZone = new Temporal.TimeZone("UTC"); // should not be used to convert datetime to PlainTime
+const date = new Temporal.PlainDate(2000, 5, 2);
+const zdt = date.toZonedDateTime({ timeZone: otherTimeZone, plainTime: datetime });
+
+assert.sameValue(zdt.microsecond, 0);
+assert.sameValue(zdt.nanosecond, 999);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..1e814437792
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.toZonedDateTime({ plainTime: datetime, timeZone: "UTC" }));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..0dd43236440
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.toZonedDateTime({ plainTime: datetime, timeZone: "UTC" }));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..c158557b7be
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/plaintime-argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => date.toZonedDateTime({ plainTime: datetime, timeZone: "UTC" }));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/prop-desc.js
new file mode 100644
index 00000000000..0259ea768be
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: The "toZonedDateTime" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.toZonedDateTime,
+ "function",
+ "`typeof PlainDate.prototype.toZonedDateTime` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "toZonedDateTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..fdadf46551a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const plainTime = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ assert.throws(RangeError, () => date.toZonedDateTime({ plainTime, timeZone }));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..85c1390193b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const plainTime = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ assert.throws(RangeError, () => date.toZonedDateTime({ plainTime, timeZone }));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..da5ce81dafb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const plainTime = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ assert.throws(TypeError, () => date.toZonedDateTime({ plainTime, timeZone }));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 00000000000..a3d4f477904
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,42 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.plaindate.prototype.tozoneddatetime step 7:
+ 7. Let _instant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _temporalDateTime_, *"compatible"*).
+ sec-temporal-builtintimezonegetinstantfor step 1:
+ 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-builtintimezonegetinstantfor step 14:
+ 14. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ sec-temporal-builtintimezonegetinstantfor step 16:
+ 16. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _later_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "2000-05-02T00:00:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ date.toZonedDateTime(timeZone);
+}, expected1);
+
+// Same, but test the other path where the time doesn't exist and
+// GetPossibleInstantsFor is called again on a later time
+
+const expected2 = [
+ "2030-01-01T00:30:00",
+ "2030-01-01T01:30:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const date = new Temporal.PlainDate(2030, 1, 1);
+ date.toZonedDateTime({ plainTime: new Temporal.PlainTime(0, 30), timeZone });
+}, expected2);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-datetime.js b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-datetime.js
new file mode 100644
index 00000000000..b5ac42731a5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-datetime.js
@@ -0,0 +1,44 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.toZonedDateTime(timeZone), "bare date-time string is not a time zone");
+assert.throws(RangeError, () => instance.toZonedDateTime({ timeZone }), "bare date-time string is not a time zone");
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result1.timeZone.id, "UTC", "date-time + Z is UTC time zone");
+const result2 = instance.toZonedDateTime({ timeZone });
+assert.sameValue(result2.timeZone.id, "UTC", "date-time + Z is UTC time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result3 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result3.timeZone.id, "-07:00", "date-time + offset is the offset time zone");
+const result4 = instance.toZonedDateTime({ timeZone });
+assert.sameValue(result4.timeZone.id, "-07:00", "date-time + offset is the offset time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30[America/Vancouver]";
+const result5 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result5.timeZone.id, "America/Vancouver", "date-time + IANA annotation is the IANA time zone");
+const result6 = instance.toZonedDateTime({ timeZone });
+assert.sameValue(result6.timeZone.id, "America/Vancouver", "date-time + IANA annotation is the IANA time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30Z[America/Vancouver]";
+const result7 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result7.timeZone.id, "America/Vancouver", "date-time + Z + IANA annotation is the IANA time zone");
+const result8 = instance.toZonedDateTime({ timeZone });
+assert.sameValue(result8.timeZone.id, "America/Vancouver", "date-time + Z + IANA annotation is the IANA time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30-07:00[America/Vancouver]";
+const result9 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result9.timeZone.id, "America/Vancouver", "date-time + offset + IANA annotation is the IANA time zone");
+const result10 = instance.toZonedDateTime({ timeZone });
+assert.sameValue(result10.timeZone.id, "America/Vancouver", "date-time + offset + IANA annotation is the IANA time zone (string in property bag)");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/argument-plaindatetime.js b/test/built-ins/Temporal/PlainDate/prototype/until/argument-plaindatetime.js
new file mode 100644
index 00000000000..930b4ccf5de
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/argument-plaindatetime.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.until
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.plaindate.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const result = date.until(datetime);
+ assert.sameValue(result.total({ unit: "nanoseconds" }), 0, "time part dropped");
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..dc983f8cb3c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.until(datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..88c0cdc3982
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => date.until(datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..326010d3c66
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => date.until(datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/basic.js b/test/built-ins/Temporal/PlainDate/prototype/until/basic.js
new file mode 100644
index 00000000000..08782e0e5a7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/basic.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Basic tests for until().
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1969, 7, 24);
+const plainDate2 = Temporal.PlainDate.from({ year: 1969, month: 10, day: 5 });
+TemporalHelpers.assertDuration(plainDate.until(plainDate2), 0, 0, 0, /* days = */ 73, 0, 0, 0, 0, 0, 0, "same year");
+
+const earlier = new Temporal.PlainDate(1969, 7, 24);
+const later = new Temporal.PlainDate(1996, 3, 3);
+TemporalHelpers.assertDuration(earlier.until(later), 0, 0, 0, /* days = */ 9719, 0, 0, 0, 0, 0, 0, "different year");
+
+TemporalHelpers.assertDuration(plainDate.until({ year: 2019, month: 7, day: 24 }), 0, 0, 0, /* days = */ 18262, 0, 0, 0, 0, 0, 0, "option bag");
+TemporalHelpers.assertDuration(plainDate.until("2019-07-24"), 0, 0, 0, /* days = */ 18262, 0, 0, 0, 0, 0, 0, "string");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/builtin.js b/test/built-ins/Temporal/PlainDate/prototype/until/builtin.js
new file mode 100644
index 00000000000..1c2f645ab30
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: >
+ Tests that Temporal.PlainDate.prototype.until
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.until),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.until),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.until),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.until.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js b/test/built-ins/Temporal/PlainDate/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 00000000000..49cfdbbc710
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.plaindate.prototype.until steps 12–13:
+ 13. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _largestUnit_).
+ 14. Let _result_ be ? CalendarDateUntil(_temporalDate_.[[Calendar]], _temporalDate_, _other_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.PlainDate(2000, 5, 2, calendar);
+ const later = new Temporal.PlainDate(2001, 6, 3, calendar);
+ earlier.until(later, { largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"],
+ weeks: ["week"],
+ days: ["day"]
+ }
+);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainDate/prototype/until/calendar-fields-iterable.js
new file mode 100644
index 00000000000..9bf9f358ae7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/calendar-fields-iterable.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const date = new Temporal.PlainDate(2000, 5, 2, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+date.until({ year: 2005, month: 6, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/calendar-id-match.js b/test/built-ins/Temporal/PlainDate/prototype/until/calendar-id-match.js
new file mode 100644
index 00000000000..9eb1c085782
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/calendar-id-match.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Calculation is performed if calendars' toString results match
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class Calendar1 extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ toString() {
+ return "A";
+ }
+}
+class Calendar2 extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ toString() {
+ return "A";
+ }
+}
+
+const plainDate1 = new Temporal.PlainDate(2000, 1, 1, new Calendar1());
+const plainDate2 = new Temporal.PlainDate(2000, 1, 2, new Calendar2());
+TemporalHelpers.assertDuration(plainDate1.until(plainDate2), 0, 0, 0, /* days = */ 1, 0, 0, 0, 0, 0, 0);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/calendar-mismatch.js b/test/built-ins/Temporal/PlainDate/prototype/until/calendar-mismatch.js
new file mode 100644
index 00000000000..481fff4f395
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/calendar-mismatch.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: RangeError thrown if calendars' toString results do not match
+features: [Temporal]
+---*/
+
+const calendar1 = { toString() { return "A"; } };
+const calendar2 = { toString() { return "B"; } };
+
+const plainDate1 = new Temporal.PlainDate(2000, 1, 1, calendar1);
+const plainDate2 = new Temporal.PlainDate(2000, 1, 1, calendar2);
+assert.throws(RangeError, () => plainDate1.until(plainDate2));
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/calendar-temporal-object.js b/test/built-ins/Temporal/PlainDate/prototype/until/calendar-temporal-object.js
new file mode 100644
index 00000000000..c14e4796743
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindate.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalDate(_other_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const date = new Temporal.PlainDate(2000, 5, 2, temporalObject);
+ date.until({ year: 2005, month: 6, day: 2, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDate/prototype/until/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..efeba2f97ca
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindate.prototype.until
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.until({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.until({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/largestunit-default.js b/test/built-ins/Temporal/PlainDate/prototype/until/largestunit-default.js
new file mode 100644
index 00000000000..ea3f6679570
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/largestunit-default.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Default value for largestUnit option is days
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const feb20 = Temporal.PlainDate.from("2020-02-01");
+const feb21 = Temporal.PlainDate.from("2021-02-01");
+TemporalHelpers.assertDuration(feb20.until(feb21), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, 0, "no options");
+TemporalHelpers.assertDuration(feb20.until(feb21, undefined), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, 0, "undefined options");
+TemporalHelpers.assertDuration(feb20.until(feb21, {}), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, 0, "no largestUnit");
+TemporalHelpers.assertDuration(feb20.until(feb21, { largestUnit: undefined }), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, 0, "undefined largestUnit");
+TemporalHelpers.assertDuration(feb20.until(feb21, { largestUnit: "days" }), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, 0, "days");
+TemporalHelpers.assertDuration(feb20.until(feb21, { largestUnit: "auto" }), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, 0, "auto");
+TemporalHelpers.assertDuration(feb20.until(feb21, () => {}), 0, 0, 0, /* days = */ 366, 0, 0, 0, 0, 0, 0, 0, "no largestUnit (function)");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/largestunit-higher-units.js b/test/built-ins/Temporal/PlainDate/prototype/until/largestunit-higher-units.js
new file mode 100644
index 00000000000..30c7ceec20c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/largestunit-higher-units.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Tests calculations with higher largestUnit than the default of 'days'
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(1969, 7, 24);
+const later = Temporal.PlainDate.from({ year: 2019, month: 7, day: 24 });
+const duration = date.until(later, { largestUnit: "years" });
+TemporalHelpers.assertDuration(duration, /* years = */ 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, "crossing epoch");
+
+const feb20 = Temporal.PlainDate.from("2020-02-01");
+const feb21 = Temporal.PlainDate.from("2021-02-01");
+TemporalHelpers.assertDuration(feb20.until(feb21, { largestUnit: "years" }), /* years = */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "start of February, years");
+TemporalHelpers.assertDuration(feb20.until(feb21, { largestUnit: "months" }), 0, /* months = */ 12, 0, 0, 0, 0, 0, 0, 0, 0, "start of February, months");
+TemporalHelpers.assertDuration(feb20.until(feb21, { largestUnit: "weeks" }), 0, 0, /* weeks = */ 52, /* days = */ 2, 0, 0, 0, 0, 0, 0, 0, "start of February, weeks");
+
+const lastFeb20 = Temporal.PlainDate.from("2020-02-29");
+const lastFeb21 = Temporal.PlainDate.from("2021-02-28");
+TemporalHelpers.assertDuration(lastFeb20.until(lastFeb21, { largestUnit: "years" }), /* years = */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "end of February, years");
+TemporalHelpers.assertDuration(lastFeb20.until(lastFeb21, { largestUnit: "months" }), 0, /* months = */ 12, 0, 0, 0, 0, 0, 0, 0, 0, "end of February, months");
+TemporalHelpers.assertDuration(lastFeb20.until(lastFeb21, { largestUnit: "weeks" }), 0, 0, /* weeks = */ 52, /* days = */ 1, 0, 0, 0, 0, 0, 0, "end of February, weeks");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/largestunit-invalid-string.js b/test/built-ins/Temporal/PlainDate/prototype/until/largestunit-invalid-string.js
new file mode 100644
index 00000000000..63b4279637c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/largestunit-invalid-string.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+const values = ["hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds", "other string"];
+for (const largestUnit of values) {
+ assert.throws(RangeError, () => earlier.until(later, { largestUnit }));
+}
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/largestunit-plurals-accepted.js b/test/built-ins/Temporal/PlainDate/prototype/until/largestunit-plurals-accepted.js
new file mode 100644
index 00000000000..69170410b65
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/largestunit-plurals-accepted.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 12);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => earlier.until(later, { largestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/largestunit-smallestunit-mismatch.js b/test/built-ins/Temporal/PlainDate/prototype/until/largestunit-smallestunit-mismatch.js
new file mode 100644
index 00000000000..7b2334722f1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/largestunit-smallestunit-mismatch.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: RangeError thrown when smallestUnit is larger than largestUnit
+features: [Temporal]
+---*/
+
+const earlier = Temporal.PlainDate.from("2019-01-08");
+const later = Temporal.PlainDate.from("2021-09-07");
+const units = ["years", "months", "weeks", "days"];
+for (let largestIdx = 1; largestIdx < units.length; largestIdx++) {
+ for (let smallestIdx = 0; smallestIdx < largestIdx; smallestIdx++) {
+ const largestUnit = units[largestIdx];
+ const smallestUnit = units[smallestIdx];
+ assert.throws(RangeError, () => earlier.until(later, { largestUnit, smallestUnit }));
+ }
+}
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/largestunit-wrong-type.js b/test/built-ins/Temporal/PlainDate/prototype/until/largestunit-wrong-type.js
new file mode 100644
index 00000000000..e53df7dff06
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/largestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "year",
+ (largestUnit) => earlier.until(later, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/length.js b/test/built-ins/Temporal/PlainDate/prototype/until/length.js
new file mode 100644
index 00000000000..be7e4a3054c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Temporal.PlainDate.prototype.until.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.until, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/name.js b/test/built-ins/Temporal/PlainDate/prototype/until/name.js
new file mode 100644
index 00000000000..0677ede45ab
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Temporal.PlainDate.prototype.until.name is "until".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.until, "name", {
+ value: "until",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/not-a-constructor.js b/test/built-ins/Temporal/PlainDate/prototype/until/not-a-constructor.js
new file mode 100644
index 00000000000..897ef84fa3a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: >
+ Temporal.PlainDate.prototype.until does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.until();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.until), false,
+ "isConstructor(Temporal.PlainDate.prototype.until)");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/options-invalid.js b/test/built-ins/Temporal/PlainDate/prototype/until/options-invalid.js
new file mode 100644
index 00000000000..7c737f9eb57
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/options-invalid.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: TypeError thrown when a primitive is passed as the options argument
+features: [Temporal]
+---*/
+
+const feb20 = Temporal.PlainDate.from("2020-02-01");
+const feb21 = Temporal.PlainDate.from("2021-02-01");
+const values = [null, true, "hello", Symbol("foo"), 1, 1n];
+for (const badOptions of values) {
+ assert.throws(TypeError, () => feb20.until(feb21, badOptions));
+}
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/until/prop-desc.js
new file mode 100644
index 00000000000..3004aae201f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: The "until" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.until,
+ "function",
+ "`typeof PlainDate.prototype.until` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "until", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-nan.js b/test/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-nan.js
new file mode 100644
index 00000000000..482c0401438
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-nan.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindate.prototype.until step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: NaN }));
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-non-integer.js b/test/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-non-integer.js
new file mode 100644
index 00000000000..a5da75f9c5b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-non-integer.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Rounding for roundingIncrement option
+info: |
+ sec-temporal-totemporalroundingincrement step 7:
+ 7. Set _increment_ to floor(ℝ(_increment_)).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+const result = earlier.until(later, { roundingIncrement: 2.5 });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, "roundingIncrement 2.5 floors to 2");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-out-of-range.js b/test/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-out-of-range.js
new file mode 100644
index 00000000000..e0da8b519de
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-out-of-range.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ sec-temporal-totemporalroundingincrement step 6:
+ 6. If _increment_ < 1 or _increment_ > _maximum_, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: Infinity }));
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-undefined.js b/test/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-undefined.js
new file mode 100644
index 00000000000..51b05721923
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-undefined.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindate.prototype.until step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+
+const explicit = earlier.until(later, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, "default roundingIncrement is 1");
+
+// See options-undefined.js for {}
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-wrong-type.js b/test/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-wrong-type.js
new file mode 100644
index 00000000000..9ee1dbefd9b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/roundingincrement-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindate.prototype.until step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2000, 5, 7);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => earlier.until(later, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/roundingmode-invalid-string.js b/test/built-ins/Temporal/PlainDate/prototype/until/roundingmode-invalid-string.js
new file mode 100644
index 00000000000..2ed4da16bb6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/roundingmode-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "microsecond", roundingMode: "other string" }));
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/roundingmode-undefined.js b/test/built-ins/Temporal/PlainDate/prototype/until/roundingmode-undefined.js
new file mode 100644
index 00000000000..683d63f4f97
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/roundingmode-undefined.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 1, 1);
+
+const later1 = new Temporal.PlainDate(2005, 2, 20);
+const explicit1 = earlier.until(later1, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit1 = earlier.until(later1, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+
+const later2 = new Temporal.PlainDate(2005, 12, 15);
+const explicit2 = earlier.until(later2, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit2 = earlier.until(later2, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/roundingmode-wrong-type.js b/test/built-ins/Temporal/PlainDate/prototype/until/roundingmode-wrong-type.js
new file mode 100644
index 00000000000..398c5b49e71
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/roundingmode-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => earlier.until(later, { smallestUnit: "year", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/smallestunit-invalid-string.js b/test/built-ins/Temporal/PlainDate/prototype/until/smallestunit-invalid-string.js
new file mode 100644
index 00000000000..86a44177491
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/smallestunit-invalid-string.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+const values = ["era", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds", "other string"];
+for (const smallestUnit of values) {
+ assert.throws(RangeError, () => earlier.until(later, { smallestUnit }));
+}
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/smallestunit-plurals-accepted.js b/test/built-ins/Temporal/PlainDate/prototype/until/smallestunit-plurals-accepted.js
new file mode 100644
index 00000000000..8a37f65f240
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/smallestunit-plurals-accepted.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 12);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => earlier.until(later, { smallestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/smallestunit-undefined.js b/test/built-ins/Temporal/PlainDate/prototype/until/smallestunit-undefined.js
new file mode 100644
index 00000000000..3e52556ca59
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/smallestunit-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+
+const explicit = earlier.until(later, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, "default smallestUnit is day");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, "default smallestUnit is day");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/smallestunit-wrong-type.js b/test/built-ins/Temporal/PlainDate/prototype/until/smallestunit-wrong-type.js
new file mode 100644
index 00000000000..34296f92ddd
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/smallestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.until
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDate(2000, 5, 2);
+const later = new Temporal.PlainDate(2001, 6, 3);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "year",
+ (smallestUnit) => earlier.until(later, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/valueOf/basic.js b/test/built-ins/Temporal/PlainDate/prototype/valueOf/basic.js
new file mode 100644
index 00000000000..e695d887abe
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/valueOf/basic.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.valueof
+description: Basic tests for valueOf().
+features: [Temporal]
+---*/
+
+const plainDate = Temporal.PlainDate.from("1963-02-13");
+const plainDate2 = Temporal.PlainDate.from("1963-02-13");
+
+assert.throws(TypeError, () => plainDate.valueOf(), "valueOf");
+assert.throws(TypeError, () => plainDate < plainDate, "<");
+assert.throws(TypeError, () => plainDate <= plainDate, "<=");
+assert.throws(TypeError, () => plainDate > plainDate, ">");
+assert.throws(TypeError, () => plainDate >= plainDate, ">=");
+assert.sameValue(plainDate === plainDate, true, "===");
+assert.sameValue(plainDate === plainDate2, false, "===");
+assert.sameValue(plainDate !== plainDate, false, "!==");
+assert.sameValue(plainDate !== plainDate2, true, "!==");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/valueOf/builtin.js b/test/built-ins/Temporal/PlainDate/prototype/valueOf/builtin.js
new file mode 100644
index 00000000000..e1474704dd4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/valueOf/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.valueof
+description: >
+ Tests that Temporal.PlainDate.prototype.valueOf
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.valueOf),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.valueOf),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.valueOf),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.valueOf.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/valueOf/length.js b/test/built-ins/Temporal/PlainDate/prototype/valueOf/length.js
new file mode 100644
index 00000000000..a9a5e465786
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/valueOf/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.valueof
+description: Temporal.PlainDate.prototype.valueOf.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.valueOf, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/valueOf/name.js b/test/built-ins/Temporal/PlainDate/prototype/valueOf/name.js
new file mode 100644
index 00000000000..baa2c7ecc63
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/valueOf/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.valueof
+description: Temporal.PlainDate.prototype.valueOf.name is "valueOf".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.valueOf, "name", {
+ value: "valueOf",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/valueOf/not-a-constructor.js b/test/built-ins/Temporal/PlainDate/prototype/valueOf/not-a-constructor.js
new file mode 100644
index 00000000000..4cfdfde1e1d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/valueOf/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.valueof
+description: >
+ Temporal.PlainDate.prototype.valueOf does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.valueOf();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.valueOf), false,
+ "isConstructor(Temporal.PlainDate.prototype.valueOf)");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/valueOf/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/valueOf/prop-desc.js
new file mode 100644
index 00000000000..05659ad2146
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/valueOf/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.valueof
+description: The "valueOf" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.valueOf,
+ "function",
+ "`typeof PlainDate.prototype.valueOf` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "valueOf", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/weekOfYear/basic.js b/test/built-ins/Temporal/PlainDate/prototype/weekOfYear/basic.js
new file mode 100644
index 00000000000..c9c7494a155
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/weekOfYear/basic.js
@@ -0,0 +1,34 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.weekofyear
+description: Basic tests for weekOfYear().
+features: [Temporal]
+---*/
+
+for (let i = 29; i <= 31; ++i) {
+ const plainDate = new Temporal.PlainDate(1975, 12, i);
+ assert.sameValue(plainDate.weekOfYear, 1, `${plainDate} should be in week 1`);
+}
+for (let i = 1; i <= 4; ++i) {
+ const plainDate = new Temporal.PlainDate(1976, 1, i);
+ assert.sameValue(plainDate.weekOfYear, 1, `${plainDate} should be in week 1`);
+}
+for (let i = 5; i <= 11; ++i) {
+ const plainDate = new Temporal.PlainDate(1976, 1, i);
+ assert.sameValue(plainDate.weekOfYear, 2, `${plainDate} should be in week 2`);
+}
+for (let i = 20; i <= 26; ++i) {
+ const plainDate = new Temporal.PlainDate(1976, 12, i);
+ assert.sameValue(plainDate.weekOfYear, 52, `${plainDate} should be in week 52`);
+}
+for (let i = 27; i <= 31; ++i) {
+ const plainDate = new Temporal.PlainDate(1976, 12, i);
+ assert.sameValue(plainDate.weekOfYear, 53, `${plainDate} should be in week 53`);
+}
+for (let i = 1; i <= 2; ++i) {
+ const plainDate = new Temporal.PlainDate(1977, 1, i);
+ assert.sameValue(plainDate.weekOfYear, 53, `${plainDate} should be in week 53`);
+}
+
diff --git a/test/built-ins/Temporal/PlainDate/prototype/weekOfYear/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/weekOfYear/prop-desc.js
new file mode 100644
index 00000000000..cbde49d1e6c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/weekOfYear/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.weekofyear
+description: The "weekOfYear" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "weekOfYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/with/basic.js b/test/built-ins/Temporal/PlainDate/prototype/with/basic.js
new file mode 100644
index 00000000000..ed953919774
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/with/basic.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Basic tests for with().
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1976, 11, 18);
+
+const withYear = plainDate.with({ year: 2019 });
+TemporalHelpers.assertPlainDate(withYear, 2019, 11, "M11", 18, "with(year)");
+
+const withMonth = plainDate.with({ month: 5 });
+TemporalHelpers.assertPlainDate(withMonth, 1976, 5, "M05", 18, "with(month)");
+
+const withMonthCode = plainDate.with({ monthCode: 'M05' });
+TemporalHelpers.assertPlainDate(withMonthCode, 1976, 5, "M05", 18, "with(monthCode)");
+
+const withDay = plainDate.with({ day: 17 });
+TemporalHelpers.assertPlainDate(withDay, 1976, 11, "M11", 17, "with(day)");
+
+const withPlural = plainDate.with({ months: 12, day: 15 });
+TemporalHelpers.assertPlainDate(withPlural, 1976, 11, "M11", 15, "with(plural)");
+
+assert.throws(RangeError, () => plainDate.with({ month: 5, monthCode: 'M06' }));
diff --git a/test/built-ins/Temporal/PlainDate/prototype/with/builtin.js b/test/built-ins/Temporal/PlainDate/prototype/with/builtin.js
new file mode 100644
index 00000000000..a0ae72fb210
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/with/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: >
+ Tests that Temporal.PlainDate.prototype.with
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.with),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.with),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.with),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.with.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/with/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainDate/prototype/with/calendar-fields-iterable.js
new file mode 100644
index 00000000000..8e5da8e0540
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/with/calendar-fields-iterable.js
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindate.prototype.with step 9:
+ 9. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+date.with({ year: 2005 });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/with/calendar-merge-fields-returns-primitive.js b/test/built-ins/Temporal/PlainDate/prototype/with/calendar-merge-fields-returns-primitive.js
new file mode 100644
index 00000000000..e6271bfcd17
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/with/calendar-merge-fields-returns-primitive.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: >
+ with() should throw a TypeError if mergeFields() returns a primitive,
+ without passing the value on to any other calendar methods
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[undefined, null, true, 3.14159, "bad value", Symbol("no"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarMergeFieldsReturnsPrimitive(primitive);
+ const instance = new Temporal.PlainDate(2000, 5, 2, calendar);
+ assert.throws(TypeError, () => instance.with({ year: 2005 }), "bad return from mergeFields() throws");
+ assert.sameValue(calendar.dateFromFieldsCallCount, 0, "dateFromFields() never called");
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/with/copies-merge-fields-object.js b/test/built-ins/Temporal/PlainDate/prototype/with/copies-merge-fields-object.js
new file mode 100644
index 00000000000..3a55993bc82
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/with/copies-merge-fields-object.js
@@ -0,0 +1,35 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: The object returned from mergeFields() is copied before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindate.prototype.with steps 13–15:
+ 13. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialDate_).
+ 14. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, «»).
+ 15. Return ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get day",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get month",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+];
+
+const calendar = TemporalHelpers.calendarMergeFieldsGetters();
+const date = new Temporal.PlainDate(2021, 3, 31, calendar);
+date.with({ year: 2022 });
+
+assert.compareArray(calendar.mergeFieldsReturnOperations, expected, "getters called on mergeFields return");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/with/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDate/prototype/with/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..07c927aca00
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/with/infinity-throws-rangeerror.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindate.prototype.with
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.with({ [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.with({ [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/with/length.js b/test/built-ins/Temporal/PlainDate/prototype/with/length.js
new file mode 100644
index 00000000000..a75deddb866
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/with/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Temporal.PlainDate.prototype.with.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.with, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/with/name.js b/test/built-ins/Temporal/PlainDate/prototype/with/name.js
new file mode 100644
index 00000000000..3c0958c4749
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/with/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Temporal.PlainDate.prototype.with.name is "with".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.with, "name", {
+ value: "with",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/with/not-a-constructor.js b/test/built-ins/Temporal/PlainDate/prototype/with/not-a-constructor.js
new file mode 100644
index 00000000000..63a7c946a37
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/with/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: >
+ Temporal.PlainDate.prototype.with does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.with();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.with), false,
+ "isConstructor(Temporal.PlainDate.prototype.with)");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/with/options-invalid.js b/test/built-ins/Temporal/PlainDate/prototype/with/options-invalid.js
new file mode 100644
index 00000000000..5222d725a67
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/with/options-invalid.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: TypeError thrown when a primitive is passed as the options argument
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(2000, 2, 2);
+[null, true, "hello", Symbol("foo"), 1, 1n].forEach((badOptions) =>
+ assert.throws(TypeError, () => plainDate.with({ day: 17 }, badOptions))
+);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/with/options-undefined.js b/test/built-ins/Temporal/PlainDate/prototype/with/options-undefined.js
new file mode 100644
index 00000000000..9c2b40bcd38
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/with/options-undefined.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 2, 2);
+const fields = { day: 31 };
+
+const explicit = date.with(fields, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = date.with(fields);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/with/order-of-operations.js b/test/built-ins/Temporal/PlainDate/prototype/with/order-of-operations.js
new file mode 100644
index 00000000000..dd319c15f78
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/with/order-of-operations.js
@@ -0,0 +1,52 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Properties on an object passed to with() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2);
+const expected = [
+ "get calendar",
+ "get timeZone",
+ "get day",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get month",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+];
+const actual = [];
+const fields = {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+ day: 1.7,
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = instance.with(argument);
+TemporalHelpers.assertPlainDate(result, 1, 1, "M01", 1);
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/with/overflow-invalid-string.js b/test/built-ins/Temporal/PlainDate/prototype/with/overflow-invalid-string.js
new file mode 100644
index 00000000000..d35f66be86b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/with/overflow-invalid-string.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isodatefromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.with step 16:
+ 16. Return ? DateFromFields(_calendar_, _fields_, _options_).
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+["", "CONSTRAIN", "balance", "other string"].forEach((overflow) =>
+ assert.throws(RangeError, () => date.with({ month: 8 }, { overflow }))
+);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/with/overflow-undefined.js b/test/built-ins/Temporal/PlainDate/prototype/with/overflow-undefined.js
new file mode 100644
index 00000000000..73c244f6298
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/with/overflow-undefined.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isodatefromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.with step 16:
+ 16. Return ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+const explicit = date.with({ month: 15 }, { overflow: undefined });
+TemporalHelpers.assertPlainDate(explicit, 2000, 12, "M12", 2, "default overflow is constrain");
+const implicit = date.with({ month: 15 }, {});
+TemporalHelpers.assertPlainDate(implicit, 2000, 12, "M12", 2, "default overflow is constrain");
+const fun = date.with({ month: 15 }, () => {});
+TemporalHelpers.assertPlainDate(fun, 2000, 12, "M12", 2, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/with/overflow-wrong-type.js b/test/built-ins/Temporal/PlainDate/prototype/with/overflow-wrong-type.js
new file mode 100644
index 00000000000..c96b1092761
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/with/overflow-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isodatefromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindate.prototype.with step 16:
+ 16. Return ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2000, 5, 2);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => date.with({ month: 8 }, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDate(result, 2000, 8, "M08", 2, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/with/plaindatelike-invalid.js b/test/built-ins/Temporal/PlainDate/prototype/with/plaindatelike-invalid.js
new file mode 100644
index 00000000000..189c334377b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/with/plaindatelike-invalid.js
@@ -0,0 +1,48 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Throws TypeError on an argument that is not a PlainDate-like property bag
+features: [Temporal]
+---*/
+
+const plainDate = new Temporal.PlainDate(1976, 11, 18);
+
+const tests = [
+ // Step 3.
+ [undefined],
+ [null],
+ [true],
+ ["2019-05-17"],
+ ["2019-05-17T12:34"],
+ ["2019-05-17T12:34Z"],
+ ["18:05:42.577"],
+ ["42"],
+ [Symbol(), "symbol"],
+ [42, "number"],
+ [42n, "bigint"],
+
+ // Step 4.
+ [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.
+ [{ year: 2021, calendar: "iso8601" }, "calendar"],
+
+ // Step 7-8.
+ [{ year: 2021, timeZone: "UTC" }, "timeZone"],
+
+ // Step 11.
+ [{}, "empty object"],
+ [{ months: 12 }, "only plural property"],
+
+];
+
+for (const [value, message = String(value)] of tests) {
+ assert.throws(TypeError, () => plainDate.with(value), message);
+}
diff --git a/test/built-ins/Temporal/PlainDate/prototype/with/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/with/prop-desc.js
new file mode 100644
index 00000000000..57acd21b22b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/with/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: The "with" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.with,
+ "function",
+ "`typeof PlainDate.prototype.with` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "with", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/with/subclassing-ignored.js b/test/built-ins/Temporal/PlainDate/prototype/with/subclassing-ignored.js
new file mode 100644
index 00000000000..9b285f87079
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/with/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.with
+description: Objects of a subclass are never created as return values for with()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDate,
+ [2000, 5, 2],
+ "with",
+ [{ day: 20 }],
+ (result) => TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 20),
+);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/withCalendar/basic.js b/test/built-ins/Temporal/PlainDate/prototype/withCalendar/basic.js
new file mode 100644
index 00000000000..6fd61bb1e98
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/withCalendar/basic.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: Basic tests for withCalendar().
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainDate = Temporal.PlainDate.from("1976-11-18");
+const calendar = Temporal.Calendar.from("iso8601");
+
+const objectResult = plainDate.withCalendar(calendar);
+TemporalHelpers.assertPlainDate(objectResult, 1976, 11, "M11", 18, "object");
+assert.sameValue(objectResult.calendar, calendar, "object calendar");
+
+const stringResult = plainDate.withCalendar("iso8601");
+TemporalHelpers.assertPlainDate(stringResult, 1976, 11, "M11", 18, "string");
+assert.sameValue(stringResult.calendar.id, "iso8601", "string calendar");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/withCalendar/builtin.js b/test/built-ins/Temporal/PlainDate/prototype/withCalendar/builtin.js
new file mode 100644
index 00000000000..722d27b5542
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/withCalendar/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: >
+ Tests that Temporal.PlainDate.prototype.withCalendar
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDate.prototype.withCalendar),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDate.prototype.withCalendar),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDate.prototype.withCalendar),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDate.prototype.withCalendar.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-temporal-object.js b/test/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-temporal-object.js
new file mode 100644
index 00000000000..dab1fdcfccb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/withCalendar/calendar-temporal-object.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindate.prototype.withcalendar step 3:
+ 3. Let _calendar_ be ? ToTemporalCalendar(_calendarLike_).
+ 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 plainDate = new Temporal.PlainDate(2000, 5, 2);
+ const result = plainDate.withCalendar(temporalObject);
+ assert.sameValue(result.calendar, calendar, "Temporal object coerced to calendar");
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/withCalendar/length.js b/test/built-ins/Temporal/PlainDate/prototype/withCalendar/length.js
new file mode 100644
index 00000000000..92df842836d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/withCalendar/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: Temporal.PlainDate.prototype.withCalendar.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.withCalendar, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/withCalendar/missing-argument.js b/test/built-ins/Temporal/PlainDate/prototype/withCalendar/missing-argument.js
new file mode 100644
index 00000000000..d5b014c1b2e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/withCalendar/missing-argument.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: RangeError thrown when calendar argument not given
+features: [Temporal]
+---*/
+
+const plainDate = Temporal.PlainDate.from("1976-11-18");
+assert.throws(RangeError, () => plainDate.withCalendar(), "missing argument");
+assert.throws(RangeError, () => plainDate.withCalendar(undefined), "undefined argument");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/withCalendar/name.js b/test/built-ins/Temporal/PlainDate/prototype/withCalendar/name.js
new file mode 100644
index 00000000000..5bdc4bcf396
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/withCalendar/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: Temporal.PlainDate.prototype.withCalendar.name is "withCalendar".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDate.prototype.withCalendar, "name", {
+ value: "withCalendar",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/withCalendar/not-a-constructor.js b/test/built-ins/Temporal/PlainDate/prototype/withCalendar/not-a-constructor.js
new file mode 100644
index 00000000000..88c261ef1ec
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/withCalendar/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: >
+ Temporal.PlainDate.prototype.withCalendar does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDate.prototype.withCalendar();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDate.prototype.withCalendar), false,
+ "isConstructor(Temporal.PlainDate.prototype.withCalendar)");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/withCalendar/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/withCalendar/prop-desc.js
new file mode 100644
index 00000000000..c841e77ac35
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/withCalendar/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: The "withCalendar" property of Temporal.PlainDate.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDate.prototype.withCalendar,
+ "function",
+ "`typeof PlainDate.prototype.withCalendar` is `function`"
+);
+
+verifyProperty(Temporal.PlainDate.prototype, "withCalendar", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDate/prototype/withCalendar/subclassing-ignored.js b/test/built-ins/Temporal/PlainDate/prototype/withCalendar/subclassing-ignored.js
new file mode 100644
index 00000000000..f8dce94d515
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/withCalendar/subclassing-ignored.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.withcalendar
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const customCalendar = {
+ era() { return undefined; },
+ eraYear() { return undefined; },
+ year() { return 1900; },
+ month() { return 2; },
+ monthCode() { return "M02"; },
+ day() { return 5; },
+ toString() { return "custom-calendar"; },
+};
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDate,
+ [2000, 5, 2],
+ "withCalendar",
+ [customCalendar],
+ (result) => {
+ TemporalHelpers.assertPlainDate(result, 1900, 2, "M02", 5);
+ assert.sameValue(result.calendar, customCalendar, "calendar result");
+ },
+);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/year/calendar-returns-infinity.js b/test/built-ins/Temporal/PlainDate/prototype/year/calendar-returns-infinity.js
new file mode 100644
index 00000000000..3a0ad9cb83c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/year/calendar-returns-infinity.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.year
+description: Getter throws if the calendar returns ±∞ from its year method
+features: [Temporal]
+---*/
+
+class InfinityCalendar extends Temporal.Calendar {
+ constructor(positive) {
+ super("iso8601");
+ this.positive = positive;
+ }
+
+ year() {
+ return this.positive ? Infinity : -Infinity;
+ }
+}
+
+const pos = new InfinityCalendar(true);
+const instance1 = new Temporal.PlainDate(2000, 5, 2, pos);
+assert.throws(RangeError, () => instance1.year);
+
+const neg = new InfinityCalendar(false);
+const instance2 = new Temporal.PlainDate(2000, 5, 2, neg);
+assert.throws(RangeError, () => instance2.year);
diff --git a/test/built-ins/Temporal/PlainDate/prototype/year/prop-desc.js b/test/built-ins/Temporal/PlainDate/prototype/year/prop-desc.js
new file mode 100644
index 00000000000..c2a757c5b76
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDate/prototype/year/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.year
+description: The "year" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "year");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDateTime/builtin.js b/test/built-ins/Temporal/PlainDateTime/builtin.js
new file mode 100644
index 00000000000..fd6a0757dab
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/builtin.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Tests that Temporal.PlainDateTime meets the requirements for built-in objects
+info: |
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.PlainDateTime.prototype,
+ "object", "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/calendar-temporal-object.js b/test/built-ins/Temporal/PlainDateTime/calendar-temporal-object.js
new file mode 100644
index 00000000000..517e5b551fc
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/calendar-temporal-object.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindatetime step 11:
+ 11. Let _calendar_ be ? ToTemporalCalendarWithISODefault(_calendarLike_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const result = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, temporalObject);
+ assert.sameValue(result.calendar, calendar, 'Temporal object coerced to calendar');
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/calendar-undefined.js b/test/built-ins/Temporal/PlainDateTime/calendar-undefined.js
new file mode 100644
index 00000000000..55340575281
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/calendar-undefined.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Calendar argument defaults to the built-in ISO 8601 calendar
+features: [Temporal]
+---*/
+
+const dateTimeArgs = [2020, 12, 24, 12, 34, 56, 123, 456, 789];
+
+Object.defineProperty(Temporal.Calendar, "from", {
+ get() {
+ throw new Test262Error("Should not get Calendar.from");
+ },
+});
+
+const dateTimeExplicit = new Temporal.PlainDateTime(...dateTimeArgs, undefined);
+assert.sameValue(dateTimeExplicit.calendar.toString(), "iso8601");
+
+const dateTimeImplicit = new Temporal.PlainDateTime(...dateTimeArgs);
+assert.sameValue(dateTimeImplicit.calendar.toString(), "iso8601");
diff --git a/test/built-ins/Temporal/PlainDateTime/compare/argument-plaindate.js b/test/built-ins/Temporal/PlainDateTime/compare/argument-plaindate.js
new file mode 100644
index 00000000000..5b3d84198c2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/compare/argument-plaindate.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Fast path for converting Temporal.PlainDate to Temporal.PlainDateTime by reading internal slots
+info: |
+ sec-temporal.plaindatetime.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalDateTime(_one_).
+ 2. Set _two_ to ? ToTemporalDateTime(_two_).
+ sec-temporal-totemporaldatetime step 2.b:
+ b. If _item_ has an [[InitializedTemporalDate]] internal slot, then
+ i. Return ? CreateTemporalDateTime(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], 0, 0, 0, 0, 0, 0, _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+TemporalHelpers.checkToTemporalPlainDateTimeFastPath((date) => {
+ const result = Temporal.PlainDateTime.compare(date, datetime);
+ assert.sameValue(result, -1, "PlainDate is converted to midnight");
+});
+
+TemporalHelpers.checkToTemporalPlainDateTimeFastPath((date) => {
+ const result = Temporal.PlainDateTime.compare(datetime, date);
+ assert.sameValue(result, 1, "PlainDate is converted to midnight");
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-negative-epochnanoseconds.js b/test/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 00000000000..ed4ac471a1c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const zoned = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+const plain = new Temporal.PlainDateTime(1969, 7, 24, 16, 50, 35, 0, 0, 1);
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const result1 = Temporal.PlainDateTime.compare(plain, zoned);
+assert.sameValue(result1, 0);
+
+const result2 = Temporal.PlainDateTime.compare(zoned, plain);
+assert.sameValue(result2, 0);
diff --git a/test/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..c10ddd89adc
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, Infinity, -Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(datetime, plain));
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(plain, datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..2326c5adb2a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(datetime, plain));
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(plain, datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..d113b03b473
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+ assert.throws(TypeError, () => Temporal.PlainDateTime.compare(datetime, plain));
+ assert.throws(TypeError, () => Temporal.PlainDateTime.compare(plain, datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/compare/builtin.js b/test/built-ins/Temporal/PlainDateTime/compare/builtin.js
new file mode 100644
index 00000000000..7e63c0bf469
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/compare/builtin.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Tests that Temporal.PlainDateTime.compare meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.compare),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.compare),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.compare),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.compare.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/compare/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainDateTime/compare/calendar-fields-iterable.js
new file mode 100644
index 00000000000..6ff46e0b12e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/compare/calendar-fields-iterable.js
@@ -0,0 +1,44 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalDateTime(_one_).
+ 2. Set _two_ to ? ToTemporalDateTime(_two_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "hour",
+ "microsecond",
+ "millisecond",
+ "minute",
+ "month",
+ "monthCode",
+ "nanosecond",
+ "second",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+Temporal.PlainDateTime.compare(
+ { year: 2000, month: 5, day: 2, calendar: calendar1 },
+ { year: 2001, month: 6, day: 3, calendar: calendar2 },
+);
+
+assert.sameValue(calendar1.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar1.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar1.iteratorExhausted[0], "iterated through the whole iterable");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainDateTime/compare/calendar-temporal-object.js b/test/built-ins/Temporal/PlainDateTime/compare/calendar-temporal-object.js
new file mode 100644
index 00000000000..2807dc0952c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/compare/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindatetime.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalDateTime(_one_).
+ 2. Set _two_ to ? ToTemporalDateTime(_two_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ Temporal.PlainDateTime.compare(
+ { year: 2000, month: 5, day: 2, calendar: temporalObject },
+ { year: 2001, month: 6, day: 3, calendar: temporalObject },
+ );
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/compare/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDateTime/compare/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..602c75fd0c6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/compare/infinity-throws-rangeerror.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in a property bag for either argument is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.compare
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const other = new Temporal.PlainDateTime(2000, 5, 2, 15);
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare({ ...base, [prop]: inf }, other), `${prop} property cannot be ${inf}`);
+
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(other, { ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls1 = [];
+ const obj1 = TemporalHelpers.toPrimitiveObserver(calls1, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare({ ...base, [prop]: obj1 }, other));
+ assert.compareArray(calls1, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+
+ const calls2 = [];
+ const obj2 = TemporalHelpers.toPrimitiveObserver(calls2, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(other, { ...base, [prop]: obj2 }));
+ assert.compareArray(calls2, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/compare/length.js b/test/built-ins/Temporal/PlainDateTime/compare/length.js
new file mode 100644
index 00000000000..debb94b6089
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/compare/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Temporal.PlainDateTime.compare.length is 2
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.compare, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/compare/name.js b/test/built-ins/Temporal/PlainDateTime/compare/name.js
new file mode 100644
index 00000000000..5c60eec7d1d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/compare/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Temporal.PlainDateTime.compare.name is "compare"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.compare, "name", {
+ value: "compare",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/compare/not-a-constructor.js b/test/built-ins/Temporal/PlainDateTime/compare/not-a-constructor.js
new file mode 100644
index 00000000000..20df8a57b47
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/compare/not-a-constructor.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: Temporal.PlainDateTime.compare does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.compare();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.compare), false,
+ "isConstructor(Temporal.PlainDateTime.compare)");
diff --git a/test/built-ins/Temporal/PlainDateTime/compare/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/compare/prop-desc.js
new file mode 100644
index 00000000000..68ec960a4a2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/compare/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: The "compare" property of Temporal.PlainDateTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.compare,
+ "function",
+ "`typeof PlainDateTime.compare` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime, "compare", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/compare/read-time-fields-before-datefromfields.js b/test/built-ins/Temporal/PlainDateTime/compare/read-time-fields-before-datefromfields.js
new file mode 100644
index 00000000000..5e5ca28556e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/compare/read-time-fields-before-datefromfields.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.compare
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalDateTime(_one_).
+ 2. Set _two_ to ? ToTemporalDateTime(_two_).
+ sec-temporal-totemporaldatetime step 2.e:
+ e. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const result = Temporal.PlainDateTime.compare(
+ { year: 2000, month: 5, day: 2, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321, calendar },
+ { year: 2000, month: 5, day: 2, hour: 6, minute: 54, second: 32, millisecond: 123, microsecond: 456, nanosecond: 789, calendar },
+);
+
+// will be 0 if the time fields are coerced to their max values due to Infinity
+assert.sameValue(result, 1, "comparison result");
diff --git a/test/built-ins/Temporal/PlainDateTime/compare/use-internal-slots.js b/test/built-ins/Temporal/PlainDateTime/compare/use-internal-slots.js
new file mode 100644
index 00000000000..deb1ff39f32
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/compare/use-internal-slots.js
@@ -0,0 +1,44 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-compareisodatetime
+description: compare() ignores the observable properties and uses internal slots
+features: [Temporal]
+---*/
+
+function CustomError() {}
+
+class AvoidGettersDateTime extends Temporal.PlainDateTime {
+ get year() {
+ throw new CustomError();
+ }
+ get month() {
+ throw new CustomError();
+ }
+ get day() {
+ throw new CustomError();
+ }
+ get hour() {
+ throw new CustomError();
+ }
+ get minute() {
+ throw new CustomError();
+ }
+ get second() {
+ throw new CustomError();
+ }
+ get millisecond() {
+ throw new CustomError();
+ }
+ get microsecond() {
+ throw new CustomError();
+ }
+ get nanosecond() {
+ throw new CustomError();
+ }
+}
+
+const one = new AvoidGettersDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const two = new AvoidGettersDateTime(2006, 3, 25, 6, 54, 32, 123, 456, 789);
+assert.sameValue(Temporal.PlainDateTime.compare(one, two), -1);
diff --git a/test/built-ins/Temporal/PlainDateTime/constructor.js b/test/built-ins/Temporal/PlainDateTime/constructor.js
new file mode 100644
index 00000000000..fcc159a757e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/constructor.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Temporal.PlainDateTime constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.PlainDateTime());
diff --git a/test/built-ins/Temporal/PlainDateTime/from/argument-plaindate.js b/test/built-ins/Temporal/PlainDateTime/from/argument-plaindate.js
new file mode 100644
index 00000000000..1994043454c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/from/argument-plaindate.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Fast path for converting Temporal.PlainDate to Temporal.PlainDateTime by reading internal slots
+info: |
+ sec-temporal.plaindatetime.from step 3:
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+ sec-temporal-totemporaldatetime step 2.b:
+ b. If _item_ has an [[InitializedTemporalDate]] internal slot, then
+ i. Return ? CreateTemporalDateTime(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], 0, 0, 0, 0, 0, 0, _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalPlainDateTimeFastPath((date, calendar) => {
+ const result = Temporal.PlainDateTime.from(date);
+ TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 0, 0, 0, 0, 0, 0, "midnight is assumed");
+ assert.sameValue(result.calendar, calendar, "calendar result");
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-balance-negative-time-units.js b/test/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 00000000000..92492b7f2f5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Negative time fields are balanced upwards if the argument is given as ZonedDateTime
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-totemporaldatetime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaindatetime.from step 3:
+ 3. Return ? ToTemporalDateTime(_temporalTime_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// exact time's epoch parts
+const tz = new Temporal.TimeZone("-00:00:00.000000002");
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+const pdt = Temporal.PlainDateTime.from(datetime);
+
+TemporalHelpers.assertPlainDateTime(pdt, 1970, 1, "M01", 1, 1, 1, 1, 1, 0, 999);
diff --git a/test/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-negative-epochnanoseconds.js b/test/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 00000000000..a70783633d1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const result = Temporal.PlainDateTime.from(datetime);
+TemporalHelpers.assertPlainDateTime(result, 1969, 7, "M07", 24, 16, 50, 35, 0, 0, 1);
diff --git a/test/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..f95ca760908
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => Temporal.PlainDateTime.from(datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..f0f685b23a9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => Temporal.PlainDateTime.from(datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..846b9d3733d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => Temporal.PlainDateTime.from(datetime));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/from/builtin.js b/test/built-ins/Temporal/PlainDateTime/from/builtin.js
new file mode 100644
index 00000000000..ea4751982a9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/from/builtin.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Tests that Temporal.PlainDateTime.from meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.from.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/from/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainDateTime/from/calendar-fields-iterable.js
new file mode 100644
index 00000000000..318c0561a1e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/from/calendar-fields-iterable.js
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.from step 3:
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "hour",
+ "microsecond",
+ "millisecond",
+ "minute",
+ "month",
+ "monthCode",
+ "nanosecond",
+ "second",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, calendar });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainDateTime/from/calendar-temporal-object.js b/test/built-ins/Temporal/PlainDateTime/from/calendar-temporal-object.js
new file mode 100644
index 00000000000..6b76c86e592
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/from/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindatetime.from step 3:
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const result = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+ assert.sameValue(result.calendar, calendar, "Temporal object coerced to calendar");
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/from/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDateTime/from/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..88d6dc58576
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/from/infinity-throws-rangeerror.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.from
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => Temporal.PlainDateTime.from({ ...base, [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainDateTime.from({ ...base, [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/from/length.js b/test/built-ins/Temporal/PlainDateTime/from/length.js
new file mode 100644
index 00000000000..96a86a176eb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/from/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Temporal.PlainDateTime.from.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/from/name.js b/test/built-ins/Temporal/PlainDateTime/from/name.js
new file mode 100644
index 00000000000..0374393ff85
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/from/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Temporal.PlainDateTime.from.name is "from"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/from/not-a-constructor.js b/test/built-ins/Temporal/PlainDateTime/from/not-a-constructor.js
new file mode 100644
index 00000000000..2c9fcd8fb83
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/from/not-a-constructor.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Temporal.PlainDateTime.from does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.from), false,
+ "isConstructor(Temporal.PlainDateTime.from)");
diff --git a/test/built-ins/Temporal/PlainDateTime/from/options-undefined.js b/test/built-ins/Temporal/PlainDateTime/from/options-undefined.js
new file mode 100644
index 00000000000..f3ee167090a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/from/options-undefined.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const fields = { year: 2000, month: 13, day: 2 };
+
+const explicit = Temporal.PlainDateTime.from(fields, undefined);
+assert.sameValue(explicit.month, 12, "default overflow is constrain");
+
+const implicit = Temporal.PlainDateTime.from(fields);
+assert.sameValue(implicit.month, 12, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainDateTime/from/order-of-operations.js b/test/built-ins/Temporal/PlainDateTime/from/order-of-operations.js
new file mode 100644
index 00000000000..b5348ffd9fc
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/from/order-of-operations.js
@@ -0,0 +1,72 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Properties on an object passed to from() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get calendar",
+ "get day",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get microsecond",
+ "get microsecond.valueOf",
+ "call microsecond.valueOf",
+ "get millisecond",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf",
+ "get minute",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get month",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get nanosecond",
+ "get nanosecond.valueOf",
+ "call nanosecond.valueOf",
+ "get second",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+];
+const actual = [];
+const fields = {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+ day: 1.7,
+ hour: 1.7,
+ minute: 1.7,
+ second: 1.7,
+ millisecond: 1.7,
+ microsecond: 1.7,
+ nanosecond: 1.7,
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ if (key === "calendar") return Temporal.Calendar.from("iso8601");
+ const result = target[key];
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = Temporal.PlainDateTime.from(argument);
+TemporalHelpers.assertPlainDateTime(result, 1, 1, "M01", 1, 1, 1, 1, 1, 1, 1);
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainDateTime/from/overflow-invalid-string.js b/test/built-ins/Temporal/PlainDateTime/from/overflow-invalid-string.js
new file mode 100644
index 00000000000..41673499f72
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/from/overflow-invalid-string.js
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-totemporaldatetime steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ 3. Else,
+ a. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindatetime.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainDateTime(2000, 5, 2, 12),
+ { year: 2000, month: 5, day: 2, hour: 12 },
+ "2000-05-02T12:00",
+];
+validValues.forEach((value) => {
+ assert.throws(RangeError, () => Temporal.PlainDateTime.from(value, { overflow: "other string" }));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/from/overflow-undefined.js b/test/built-ins/Temporal/PlainDateTime/from/overflow-undefined.js
new file mode 100644
index 00000000000..d26c5c96ceb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/from/overflow-undefined.js
@@ -0,0 +1,45 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-totemporaldatetime steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ 3. Else,
+ a. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindatetime.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainDateTime(2000, 5, 2, 12),
+ "2000-05-02T12:00",
+];
+validValues.forEach((value) => {
+ const explicit = Temporal.PlainDateTime.from(value, { overflow: undefined });
+ TemporalHelpers.assertPlainDateTime(explicit, 2000, 5, "M05", 2, 12, 0, 0, 0, 0, 0, "overflow is ignored");
+ const implicit = Temporal.PlainDateTime.from(value, {});
+ TemporalHelpers.assertPlainDateTime(implicit, 2000, 5, "M05", 2, 12, 0, 0, 0, 0, 0, "overflow is ignored");
+});
+
+const propertyBag = { year: 2000, month: 13, day: 34, hour: 12 };
+const explicit = Temporal.PlainDateTime.from(propertyBag, { overflow: undefined });
+TemporalHelpers.assertPlainDateTime(explicit, 2000, 12, "M12", 31, 12, 0, 0, 0, 0, 0, "default overflow is constrain");
+const implicit = Temporal.PlainDateTime.from(propertyBag, {});
+TemporalHelpers.assertPlainDateTime(implicit, 2000, 12, "M12", 31, 12, 0, 0, 0, 0, 0, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainDateTime/from/overflow-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/from/overflow-wrong-type.js
new file mode 100644
index 00000000000..f306d087a34
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/from/overflow-wrong-type.js
@@ -0,0 +1,63 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-totemporaldatetime steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ 3. Else,
+ a. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindatetime.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainDateTime(2000, 5, 2, 12),
+ "2000-05-02T12:00",
+];
+validValues.forEach((value) => TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => Temporal.PlainDateTime.from(value, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 0, 0, 0, 0, 0, descr),
+));
+
+// See TemporalHelpers.checkStringOptionWrongType(); this code path has
+// different expectations for observable calls
+const propertyBag = { year: 2000, month: 5, day: 2, hour: 12 };
+
+assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: null }), "null");
+assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: true }), "true");
+assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: false }), "false");
+assert.throws(TypeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: Symbol() }), "symbol");
+assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: 2n }), "bigint");
+assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: {} }), "plain object");
+
+// toString property is read once by Calendar.dateFromFields() in the builtin
+// calendars, to get the option value for the date part, and then once again
+// internally to get the option value for the time part.
+const expected = [
+ "get overflow.toString",
+ "call overflow.toString",
+ "get overflow.toString",
+ "call overflow.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");
+const result = Temporal.PlainDateTime.from(propertyBag, { overflow: observer });
+TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 0, 0, 0, 0, 0, "object with toString");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainDateTime/from/parser.js b/test/built-ins/Temporal/PlainDateTime/from/parser.js
new file mode 100644
index 00000000000..54021112977
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/from/parser.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDateTime.from accepts a custom timezone that starts with "c".
+esid: sec-temporal.plaindatetime.from
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const dateTime = Temporal.PlainDateTime.from("2020-01-01T00:00:00+01:00[Custom]");
+TemporalHelpers.assertPlainDateTime(dateTime, 2020, 1, "M01", 1, 0, 0, 0, 0, 0, 0);
diff --git a/test/built-ins/Temporal/PlainDateTime/from/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/from/prop-desc.js
new file mode 100644
index 00000000000..2dbf3d09174
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/from/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: The "from" property of Temporal.PlainDateTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.from,
+ "function",
+ "`typeof PlainDateTime.from` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/from/read-time-fields-before-datefromfields.js b/test/built-ins/Temporal/PlainDateTime/from/read-time-fields-before-datefromfields.js
new file mode 100644
index 00000000000..e24ca12d4a6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/from/read-time-fields-before-datefromfields.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.from step 3:
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+ sec-temporal-totemporaldatetime step 2.e:
+ e. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321, calendar });
+
+assert.sameValue(datetime.hour, 12, "hour value");
+assert.sameValue(datetime.minute, 34, "minute value");
+assert.sameValue(datetime.second, 56, "second value");
+assert.sameValue(datetime.millisecond, 987, "millisecond value");
+assert.sameValue(datetime.microsecond, 654, "microsecond value");
+assert.sameValue(datetime.nanosecond, 321, "nanosecond value");
diff --git a/test/built-ins/Temporal/PlainDateTime/from/subclassing-ignored.js b/test/built-ins/Temporal/PlainDateTime/from/subclassing-ignored.js
new file mode 100644
index 00000000000..2b7980a46fd
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/from/subclassing-ignored.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.PlainDateTime,
+ "from",
+ ["2000-05-02T12:34:56.987654321"],
+ (result) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 321),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/hour-undefined.js b/test/built-ins/Temporal/PlainDateTime/hour-undefined.js
new file mode 100644
index 00000000000..e42831dcd6f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/hour-undefined.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Hour argument defaults to 0 if not given
+features: [Temporal]
+---*/
+
+const args = [2000, 5, 2];
+
+const explicit = new Temporal.PlainDateTime(...args, undefined);
+assert.sameValue(explicit.hour, 0, "hour default argument");
+
+const implicit = new Temporal.PlainDateTime(...args);
+assert.sameValue(implicit.hour, 0, "hour default argument");
diff --git a/test/built-ins/Temporal/PlainDateTime/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDateTime/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..69bda72ef9f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/infinity-throws-rangeerror.js
@@ -0,0 +1,162 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDateTime throws a RangeError if any value is Infinity
+esid: sec-temporal.plaindatetime
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.PlainDateTime(Infinity, 1, 1));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, Infinity, 1));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite year",
+ [O(Infinity, "year"), O(1, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf"
+ ]
+ ],
+ [
+ "infinite month",
+ [O(2, "year"), O(Infinity, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf"
+ ]
+ ],
+ [
+ "infinite day",
+ [O(2, "year"), O(1, "month"), O(Infinity, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"
+ ]
+ ],
+ [
+ "infinite hour",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(Infinity, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf"
+ ]
+ ],
+ [
+ "infinite minute",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(Infinity, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf"
+ ]
+ ],
+ [
+ "infinite second",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(Infinity, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf"
+ ]
+ ],
+ [
+ "infinite millisecond",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(1, "second"), O(Infinity, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf"
+ ]
+ ],
+ [
+ "infinite microsecond",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(Infinity, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf",
+ "get microsecond.valueOf",
+ "call microsecond.valueOf"
+ ]
+ ],
+ [
+ "infinite nanosecond",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(Infinity, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf",
+ "get microsecond.valueOf",
+ "call microsecond.valueOf",
+ "get nanosecond.valueOf",
+ "call nanosecond.valueOf"
+ ]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainDateTime(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
diff --git a/test/built-ins/Temporal/PlainDateTime/length.js b/test/built-ins/Temporal/PlainDateTime/length.js
new file mode 100644
index 00000000000..e20acd28316
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Temporal.PlainDateTime.length is 3
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime, "length", {
+ value: 3,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/microsecond-undefined.js b/test/built-ins/Temporal/PlainDateTime/microsecond-undefined.js
new file mode 100644
index 00000000000..83004a31301
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/microsecond-undefined.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Microsecond argument defaults to 0 if not given
+features: [Temporal]
+---*/
+
+const args = [2000, 5, 2, 12, 34, 56, 123];
+
+const explicit = new Temporal.PlainDateTime(...args, undefined);
+assert.sameValue(explicit.microsecond, 0, "microsecond default argument");
+
+const implicit = new Temporal.PlainDateTime(...args);
+assert.sameValue(implicit.microsecond, 0, "microsecond default argument");
diff --git a/test/built-ins/Temporal/PlainDateTime/millisecond-undefined.js b/test/built-ins/Temporal/PlainDateTime/millisecond-undefined.js
new file mode 100644
index 00000000000..de0c1b84476
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/millisecond-undefined.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Millisecond argument defaults to 0 if not given
+features: [Temporal]
+---*/
+
+const args = [2000, 5, 2, 12, 34, 56];
+
+const explicit = new Temporal.PlainDateTime(...args, undefined);
+assert.sameValue(explicit.millisecond, 0, "millisecond default argument");
+
+const implicit = new Temporal.PlainDateTime(...args);
+assert.sameValue(implicit.millisecond, 0, "millisecond default argument");
diff --git a/test/built-ins/Temporal/PlainDateTime/minute-undefined.js b/test/built-ins/Temporal/PlainDateTime/minute-undefined.js
new file mode 100644
index 00000000000..9ac682fc958
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/minute-undefined.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Minute argument defaults to 0 if not given
+features: [Temporal]
+---*/
+
+const args = [2000, 5, 2, 12];
+
+const explicit = new Temporal.PlainDateTime(...args, undefined);
+assert.sameValue(explicit.minute, 0, "minute default argument");
+
+const implicit = new Temporal.PlainDateTime(...args);
+assert.sameValue(implicit.minute, 0, "minute default argument");
diff --git a/test/built-ins/Temporal/PlainDateTime/missing-arguments.js b/test/built-ins/Temporal/PlainDateTime/missing-arguments.js
new file mode 100644
index 00000000000..7439ac23056
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/missing-arguments.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: RangeError thrown after processing given args when invoked without all required args
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "valueOf year",
+ "valueOf month",
+];
+const actual = [];
+const args = [
+ { valueOf() { actual.push("valueOf year"); return 1; } },
+ { valueOf() { actual.push("valueOf month"); return 1; } },
+];
+
+assert.throws(RangeError, () => new Temporal.PlainDateTime(...args));
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainDateTime/name.js b/test/built-ins/Temporal/PlainDateTime/name.js
new file mode 100644
index 00000000000..f5d13c8470b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Temporal.PlainDateTime.name is "PlainDateTime"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime, "name", {
+ value: "PlainDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/nanosecond-undefined.js b/test/built-ins/Temporal/PlainDateTime/nanosecond-undefined.js
new file mode 100644
index 00000000000..f3f8b52c5d4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/nanosecond-undefined.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Nanosecond argument defaults to 0 if not given
+features: [Temporal]
+---*/
+
+const args = [2000, 5, 2, 12, 34, 56, 123, 456];
+
+const explicit = new Temporal.PlainDateTime(...args, undefined);
+assert.sameValue(explicit.nanosecond, 0, "nanosecond default argument");
+
+const implicit = new Temporal.PlainDateTime(...args);
+assert.sameValue(implicit.nanosecond, 0, "nanosecond default argument");
diff --git a/test/built-ins/Temporal/PlainDateTime/negative-infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDateTime/negative-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..bf7ef129910
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,162 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDateTime throws a RangeError if any value is -Infinity
+esid: sec-temporal.plaindatetime
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.PlainDateTime(-Infinity, 1, 1));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, -Infinity, 1));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, -Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite year",
+ [O(-Infinity, "year"), O(1, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf"
+ ]
+ ],
+ [
+ "infinite month",
+ [O(2, "year"), O(-Infinity, "month"), O(1, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf"
+ ]
+ ],
+ [
+ "infinite day",
+ [O(2, "year"), O(1, "month"), O(-Infinity, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"
+ ]
+ ],
+ [
+ "infinite hour",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(-Infinity, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf"
+ ]
+ ],
+ [
+ "infinite minute",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(-Infinity, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf"
+ ]
+ ],
+ [
+ "infinite second",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(-Infinity, "second"), O(1, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf"
+ ]
+ ],
+ [
+ "infinite millisecond",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(1, "second"), O(-Infinity, "millisecond"), O(1, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf"
+ ]
+ ],
+ [
+ "infinite microsecond",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(-Infinity, "microsecond"), O(1, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf",
+ "get microsecond.valueOf",
+ "call microsecond.valueOf"
+ ]
+ ],
+ [
+ "infinite nanosecond",
+ [O(2, "year"), O(1, "month"), O(1, "day"), O(1, "hour"), O(1, "minute"), O(1, "second"), O(1, "millisecond"), O(1, "microsecond"), O(-Infinity, "nanosecond")],
+ [
+ "get year.valueOf",
+ "call year.valueOf",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf",
+ "get microsecond.valueOf",
+ "call microsecond.valueOf",
+ "get nanosecond.valueOf",
+ "call nanosecond.valueOf"
+ ]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainDateTime(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
diff --git a/test/built-ins/Temporal/PlainDateTime/order-of-operations.js b/test/built-ins/Temporal/PlainDateTime/order-of-operations.js
new file mode 100644
index 00000000000..4ebf7019165
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/order-of-operations.js
@@ -0,0 +1,39 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Arguments are converted to primitives in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "get (argument 0).valueOf",
+ "call (argument 0).valueOf",
+ "get (argument 1).valueOf",
+ "call (argument 1).valueOf",
+ "get (argument 2).valueOf",
+ "call (argument 2).valueOf",
+ "get (argument 3).valueOf",
+ "call (argument 3).valueOf",
+ "get (argument 4).valueOf",
+ "call (argument 4).valueOf",
+ "get (argument 5).valueOf",
+ "call (argument 5).valueOf",
+ "get (argument 6).valueOf",
+ "call (argument 6).valueOf",
+ "get (argument 7).valueOf",
+ "call (argument 7).valueOf",
+ "get (argument 8).valueOf",
+ "call (argument 8).valueOf",
+];
+
+const dateTimeArgs = [2020, 12, 24, 12, 34, 56, 123, 456, 789].map((value, idx) =>
+ TemporalHelpers.toPrimitiveObserver(actual, value, `(argument ${idx})`));
+
+const dateTime = new Temporal.PlainDateTime(...dateTimeArgs, "iso8601");
+assert.compareArray(actual, expected);
+
+TemporalHelpers.assertPlainDateTime(dateTime, 2020, 12, "M12", 24, 12, 34, 56, 123, 456, 789);
diff --git a/test/built-ins/Temporal/PlainDateTime/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prop-desc.js
new file mode 100644
index 00000000000..837f42c4f64
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: The "PlainDateTime" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime,
+ "function",
+ "`typeof PlainDateTime` is `function`"
+);
+
+verifyProperty(Temporal, "PlainDateTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/add/argument-not-object.js b/test/built-ins/Temporal/PlainDateTime/prototype/add/argument-not-object.js
new file mode 100644
index 00000000000..27f9fec46f6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/add/argument-not-object.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Passing a primitive other than string to add() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+assert.throws(RangeError, () => instance.add(undefined), "undefined");
+assert.throws(RangeError, () => instance.add(null), "null");
+assert.throws(RangeError, () => instance.add(true), "boolean");
+assert.throws(RangeError, () => instance.add(""), "empty string");
+assert.throws(TypeError, () => instance.add(Symbol()), "Symbol");
+assert.throws(RangeError, () => instance.add(7), "number");
+assert.throws(RangeError, () => instance.add(7n), "bigint");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/add/argument-string-negative-fractional-units.js b/test/built-ins/Temporal/PlainDateTime/prototype/add/argument-string-negative-fractional-units.js
new file mode 100644
index 00000000000..093b91ecc13
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/add/argument-string-negative-fractional-units.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+
+const resultHours = instance.add("-PT24.567890123H");
+TemporalHelpers.assertPlainDateTime(resultHours, 2000, 4, "M04", 30, 23, 25, 55, 595, 557, 201, "negative fractional hours");
+
+const resultMinutes = instance.add("-PT1440.567890123M");
+TemporalHelpers.assertPlainDateTime(resultMinutes, 2000, 4, "M04", 30, 23, 59, 25, 926, 592, 621, "negative fractional minutes");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/add/argument-string.js b/test/built-ins/Temporal/PlainDateTime/prototype/add/argument-string.js
new file mode 100644
index 00000000000..f7668e7dd8b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/add/argument-string.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+const result = instance.add("P3D");
+TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 5, 0, 34, 56, 987, 654, 321);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/add/balance-negative-time-units.js b/test/built-ins/Temporal/PlainDateTime/prototype/add/balance-negative-time-units.js
new file mode 100644
index 00000000000..91e5749fb10
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/add/balance-negative-time-units.js
@@ -0,0 +1,49 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-addtime step 8:
+ 8. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-adddatetime step 1:
+ 1. Let _timeResult_ be ? AddTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal.plaindatetime.prototype.add step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1996, 5, 2, 1, 1, 1, 1, 1, 1);
+
+const result1 = datetime.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainDateTime(result1, 1996, 5, "M05", 2, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = datetime.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainDateTime(result2, 1996, 5, "M05", 2, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = datetime.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainDateTime(result3, 1996, 5, "M05", 2, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = datetime.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainDateTime(result4, 1996, 5, "M05", 2, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = datetime.add(new Temporal.Duration(0, 0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainDateTime(result5, 1996, 5, "M05", 2, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+const result6 = datetime.add(new Temporal.Duration(0, 0, 0, 0, -2));
+TemporalHelpers.assertPlainDateTime(result6, 1996, 5, "M05", 1, 23, 1, 1, 1, 1, 1, "hours balance");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/add/builtin.js b/test/built-ins/Temporal/PlainDateTime/prototype/add/builtin.js
new file mode 100644
index 00000000000..b78a76eebf1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/add/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: >
+ Tests that Temporal.PlainDateTime.prototype.add
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.add),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.add),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.add),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.add.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/add/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDateTime/prototype/add/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..86e137d37af
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/add/infinity-throws-rangeerror.js
@@ -0,0 +1,35 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDateTime.prototype.add throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.plaindatetime.prototype.add
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/add/length.js b/test/built-ins/Temporal/PlainDateTime/prototype/add/length.js
new file mode 100644
index 00000000000..f2ad1dab94f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/add/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Temporal.PlainDateTime.prototype.add.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.add, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/add/name.js b/test/built-ins/Temporal/PlainDateTime/prototype/add/name.js
new file mode 100644
index 00000000000..6b981ea2aa8
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/add/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Temporal.PlainDateTime.prototype.add.name is "add".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.add, "name", {
+ value: "add",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/add/negative-infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDateTime/prototype/add/negative-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..aba85e760fb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/add/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,35 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDateTime.prototype.add throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.plaindatetime.prototype.add
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: -Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/add/non-integer-throws-rangeerror.js b/test/built-ins/Temporal/PlainDateTime/prototype/add/non-integer-throws-rangeerror.js
new file mode 100644
index 00000000000..6b09407c4da
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/add/non-integer-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.add({ [field]: -1.5 }));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/add/not-a-constructor.js b/test/built-ins/Temporal/PlainDateTime/prototype/add/not-a-constructor.js
new file mode 100644
index 00000000000..c33f18d47fe
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/add/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: >
+ Temporal.PlainDateTime.prototype.add does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.add();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.add), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.add)");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/add/options-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/add/options-undefined.js
new file mode 100644
index 00000000000..3c1795c636a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/add/options-undefined.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 1, 31, 12, 34, 56, 987, 654, 321);
+const duration = { months: 1 };
+
+const explicit = datetime.add(duration, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = datetime.add(duration);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/add/order-of-operations.js b/test/built-ins/Temporal/PlainDateTime/prototype/add/order-of-operations.js
new file mode 100644
index 00000000000..fb436b2e1b1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/add/order-of-operations.js
@@ -0,0 +1,74 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Properties on an object passed to add() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const expected = [
+ "get days",
+ "get days.valueOf",
+ "call days.valueOf",
+ "get hours",
+ "get hours.valueOf",
+ "call hours.valueOf",
+ "get microseconds",
+ "get microseconds.valueOf",
+ "call microseconds.valueOf",
+ "get milliseconds",
+ "get milliseconds.valueOf",
+ "call milliseconds.valueOf",
+ "get minutes",
+ "get minutes.valueOf",
+ "call minutes.valueOf",
+ "get months",
+ "get months.valueOf",
+ "call months.valueOf",
+ "get nanoseconds",
+ "get nanoseconds.valueOf",
+ "call nanoseconds.valueOf",
+ "get seconds",
+ "get seconds.valueOf",
+ "call seconds.valueOf",
+ "get weeks",
+ "get weeks.valueOf",
+ "call weeks.valueOf",
+ "get years",
+ "get years.valueOf",
+ "call years.valueOf",
+];
+const actual = [];
+const fields = {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = instance.add(argument);
+TemporalHelpers.assertPlainDateTime(result, 2001, 6, "M06", 10, 13, 35, 57, 988, 655, 322);
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/add/overflow-invalid-string.js b/test/built-ins/Temporal/PlainDateTime/prototype/add/overflow-invalid-string.js
new file mode 100644
index 00000000000..2ff326b8d67
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/add/overflow-invalid-string.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-adddatetime step 4:
+ 4. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.plaindatetime.prototype.add step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12);
+const duration = new Temporal.Duration(3, 3, 0, 3, 3);
+assert.throws(RangeError, () => datetime.add(duration, { overflow: "other string" }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/add/overflow-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/add/overflow-undefined.js
new file mode 100644
index 00000000000..752304f435b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/add/overflow-undefined.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-adddatetime step 4:
+ 4. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.plaindatetime.prototype.add step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 31, 12);
+const duration = new Temporal.Duration(3, 1);
+
+const explicit = datetime.add(duration, { overflow: undefined });
+TemporalHelpers.assertPlainDateTime(explicit, 2003, 6, "M06", 30, 12, 0, 0, 0, 0, 0, "default overflow is constrain");
+const implicit = datetime.add(duration, {});
+TemporalHelpers.assertPlainDateTime(implicit, 2003, 6, "M06", 30, 12, 0, 0, 0, 0, 0, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/add/overflow-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/add/overflow-wrong-type.js
new file mode 100644
index 00000000000..f26f923ed59
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/add/overflow-wrong-type.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-adddatetime step 4:
+ 4. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.plaindatetime.prototype.add step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12);
+const duration = new Temporal.Duration(3, 3, 0, 3, 3);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => datetime.add(duration, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDateTime(result, 2003, 8, "M08", 5, 15, 0, 0, 0, 0, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/add/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/add/prop-desc.js
new file mode 100644
index 00000000000..eb9fca91158
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/add/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: The "add" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.add,
+ "function",
+ "`typeof PlainDateTime.prototype.add` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "add", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/add/subclassing-ignored.js b/test/built-ins/Temporal/PlainDateTime/prototype/add/subclassing-ignored.js
new file mode 100644
index 00000000000..7fd6fc8783c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/add/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.add
+description: Objects of a subclass are never created as return values for add()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDateTime,
+ [2000, 5, 2, 12, 34, 56, 987, 654, 321],
+ "add",
+ [{ nanoseconds: 1 }],
+ (result) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 322),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/calendar/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/calendar/prop-desc.js
new file mode 100644
index 00000000000..9d3342896cb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/calendar/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.calendar
+description: The "calendar" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "calendar");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/day/calendar-returns-infinity.js b/test/built-ins/Temporal/PlainDateTime/prototype/day/calendar-returns-infinity.js
new file mode 100644
index 00000000000..5a97c2e9bbe
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/day/calendar-returns-infinity.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.day
+description: Getter throws if the calendar returns ±∞ from its day method
+features: [Temporal]
+---*/
+
+class InfinityCalendar extends Temporal.Calendar {
+ constructor(positive) {
+ super("iso8601");
+ this.positive = positive;
+ }
+
+ day() {
+ return this.positive ? Infinity : -Infinity;
+ }
+}
+
+const pos = new InfinityCalendar(true);
+const instance1 = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321, pos);
+assert.throws(RangeError, () => instance1.day);
+
+const neg = new InfinityCalendar(false);
+const instance2 = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321, neg);
+assert.throws(RangeError, () => instance2.day);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/day/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/day/prop-desc.js
new file mode 100644
index 00000000000..926d1243e4d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/day/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.day
+description: The "day" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "day");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/prop-desc.js
new file mode 100644
index 00000000000..83a55cb2b66
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/dayOfWeek/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.dayofweek
+description: The "dayOfWeek" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "dayOfWeek");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/prop-desc.js
new file mode 100644
index 00000000000..705ea416079
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/dayOfYear/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.dayofyear
+description: The "dayOfYear" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "dayOfYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/prop-desc.js
new file mode 100644
index 00000000000..626366dbdc7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/daysInMonth/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.daysinmonth
+description: The "daysInMonth" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "daysInMonth");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/prop-desc.js
new file mode 100644
index 00000000000..d5ca6889c7f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/daysInWeek/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.daysinweek
+description: The "daysInWeek" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "daysInWeek");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/daysInYear/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/daysInYear/prop-desc.js
new file mode 100644
index 00000000000..d5aa7a64a48
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/daysInYear/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.daysinyear
+description: The "daysInYear" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "daysInYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-plaindate.js b/test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-plaindate.js
new file mode 100644
index 00000000000..9af4b1969db
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-plaindate.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Fast path for converting Temporal.PlainDate to Temporal.PlainDateTime by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.b:
+ b. If _item_ has an [[InitializedTemporalDate]] internal slot, then
+ i. Return ? CreateTemporalDateTime(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], 0, 0, 0, 0, 0, 0, _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalPlainDateTimeFastPath((date, calendar) => {
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 0, 0, 0, 0, 0, 0, calendar);
+ assert(datetime.equals(date));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-wrong-type.js
new file mode 100644
index 00000000000..2ec4c776d5d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-wrong-type.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Appropriate error thrown when argument cannot be converted to a valid string
+features: [Symbol, Temporal]
+---*/
+
+const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+
+assert.throws(RangeError, () => instance.equals(undefined), "undefined");
+assert.throws(RangeError, () => instance.equals(null), "null");
+assert.throws(RangeError, () => instance.equals(true), "true");
+assert.throws(RangeError, () => instance.equals(""), "empty string");
+assert.throws(TypeError, () => instance.equals(Symbol()), "symbol");
+assert.throws(RangeError, () => instance.equals(1), "1");
+assert.throws(TypeError, () => instance.equals({}), "plain object");
+assert.throws(TypeError, () => instance.equals(Temporal.PlainDateTime), "Temporal.PlainDateTime");
+assert.throws(TypeError, () => instance.equals(Temporal.PlainDateTime.prototype), "Temporal.PlainDateTime.prototype");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js b/test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 00000000000..da4d9c59c6a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,40 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Negative time fields are balanced upwards if the argument is given as ZonedDateTime
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-totemporaldatetime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaindatetime.prototype.until step 3:
+ 3. Set _other_ ? ToTemporalDateTime(_other_).
+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 = new Temporal.TimeZone("-00:00:00.000000002");
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+assert(new Temporal.PlainDateTime(1970, 1, 1, 1, 1, 1, 1, 0, 999).equals(datetime));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js b/test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 00000000000..58e9ba6ca0e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const instance = new Temporal.PlainDateTime(1969, 7, 24, 16, 50, 35, 0, 0, 1);
+const result = instance.equals(datetime);
+assert.sameValue(result, true);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..53f040d0ff7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.equals(zoned));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..a9a4119bd40
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.equals(zoned));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..ec2fa6d0212
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => plain.equals(zoned));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/equals/builtin.js b/test/built-ins/Temporal/PlainDateTime/prototype/equals/builtin.js
new file mode 100644
index 00000000000..cc99f2a9ac4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/equals/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: >
+ Tests that Temporal.PlainDateTime.prototype.equals
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.equals),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.equals),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.equals),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.equals.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-fields-iterable.js
new file mode 100644
index 00000000000..2dd05536938
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-fields-iterable.js
@@ -0,0 +1,39 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "hour",
+ "microsecond",
+ "millisecond",
+ "minute",
+ "month",
+ "monthCode",
+ "nanosecond",
+ "second",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+datetime.equals({ year: 2005, month: 6, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-temporal-object.js b/test/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-temporal-object.js
new file mode 100644
index 00000000000..553475ccc6e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, temporalObject);
+ datetime.equals({ year: 2005, month: 6, day: 2, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/equals/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDateTime/prototype/equals/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..f2008805029
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/equals/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.prototype.equals
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15);
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/equals/length.js b/test/built-ins/Temporal/PlainDateTime/prototype/equals/length.js
new file mode 100644
index 00000000000..f88871e792c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/equals/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Temporal.PlainDateTime.prototype.equals.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.equals, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/equals/name.js b/test/built-ins/Temporal/PlainDateTime/prototype/equals/name.js
new file mode 100644
index 00000000000..992bab80b0f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/equals/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: Temporal.PlainDateTime.prototype.equals.name is "equals".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.equals, "name", {
+ value: "equals",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/equals/not-a-constructor.js b/test/built-ins/Temporal/PlainDateTime/prototype/equals/not-a-constructor.js
new file mode 100644
index 00000000000..407ae124aac
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/equals/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: >
+ Temporal.PlainDateTime.prototype.equals does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.equals();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.equals), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.equals)");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/equals/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/equals/prop-desc.js
new file mode 100644
index 00000000000..57cd21cf7c2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/equals/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: The "equals" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.equals,
+ "function",
+ "`typeof PlainDateTime.prototype.equals` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "equals", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/equals/read-time-fields-before-datefromfields.js b/test/built-ins/Temporal/PlainDateTime/prototype/equals/read-time-fields-before-datefromfields.js
new file mode 100644
index 00000000000..3a6ea884ab4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/equals/read-time-fields-before-datefromfields.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.equals
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.e:
+ e. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = new Temporal.PlainDateTime(2021, 3, 31, 12, 34, 56, 987, 654, 321, calendar);
+const result = datetime.equals({ year: 2021, month: 3, day: 31, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321, calendar });
+
+assert(result, "time fields are not modified");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/builtin.js b/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/builtin.js
new file mode 100644
index 00000000000..6e17b1b5f62
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getisofields
+description: >
+ Tests that Temporal.PlainDateTime.prototype.getISOFields
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.getISOFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.getISOFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.getISOFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.getISOFields.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-names.js b/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-names.js
new file mode 100644
index 00000000000..f444e829d57
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-names.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getisofields
+description: Correct field names on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const result = datetime.getISOFields();
+assert.sameValue(result.isoYear, 2000, "isoYear result");
+assert.sameValue(result.isoMonth, 5, "isoMonth result");
+assert.sameValue(result.isoDay, 2, "isoDay result");
+assert.sameValue(result.isoHour, 12, "isoHour result");
+assert.sameValue(result.isoMinute, 34, "isoMinute result");
+assert.sameValue(result.isoSecond, 56, "isoSecond result");
+assert.sameValue(result.isoMillisecond, 987, "isoMillisecond result");
+assert.sameValue(result.isoMicrosecond, 654, "isoMicrosecond result");
+assert.sameValue(result.isoNanosecond, 321, "isoNanosecond result");
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-prop-desc.js
new file mode 100644
index 00000000000..fdad48faf11
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-prop-desc.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getisofields
+description: Properties on the returned object have the correct descriptor
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoHour",
+ "isoMicrosecond",
+ "isoMillisecond",
+ "isoMinute",
+ "isoMonth",
+ "isoNanosecond",
+ "isoSecond",
+ "isoYear",
+];
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const result = datetime.getISOFields();
+
+for (const property of expected) {
+ verifyProperty(result, property, {
+ writable: true,
+ enumerable: true,
+ configurable: true,
+ });
+}
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-traversal-order.js b/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-traversal-order.js
new file mode 100644
index 00000000000..ba6f19faf21
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/field-traversal-order.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getisofields
+description: Properties added in correct order to object returned from getISOFields
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoHour",
+ "isoMicrosecond",
+ "isoMillisecond",
+ "isoMinute",
+ "isoMonth",
+ "isoNanosecond",
+ "isoSecond",
+ "isoYear",
+];
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const result = datetime.getISOFields();
+
+assert.compareArray(Object.keys(result), expected);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/length.js b/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/length.js
new file mode 100644
index 00000000000..463e3b0b16d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getisofields
+description: Temporal.PlainDateTime.prototype.getISOFields.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.getISOFields, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/name.js b/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/name.js
new file mode 100644
index 00000000000..ec6793d108a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getisofields
+description: Temporal.PlainDateTime.prototype.getISOFields.name is "getISOFields".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.getISOFields, "name", {
+ value: "getISOFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/not-a-constructor.js b/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/not-a-constructor.js
new file mode 100644
index 00000000000..7c459d11d42
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getisofields
+description: >
+ Temporal.PlainDateTime.prototype.getISOFields does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.getISOFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.getISOFields), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.getISOFields)");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/prop-desc.js
new file mode 100644
index 00000000000..c77649c9c47
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/getISOFields/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.getisofields
+description: The "getISOFields" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.getISOFields,
+ "function",
+ "`typeof PlainDateTime.prototype.getISOFields` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "getISOFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/hour/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/hour/prop-desc.js
new file mode 100644
index 00000000000..c1c5533f387
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/hour/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.hour
+description: The "hour" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "hour");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/prop-desc.js
new file mode 100644
index 00000000000..a6298f1f96d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/inLeapYear/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.inleapyear
+description: The "inLeapYear" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "inLeapYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/microsecond/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/microsecond/prop-desc.js
new file mode 100644
index 00000000000..ea2424ea998
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/microsecond/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.microsecond
+description: The "microsecond" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "microsecond");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/millisecond/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/millisecond/prop-desc.js
new file mode 100644
index 00000000000..e12d330aedd
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/millisecond/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.millisecond
+description: The "millisecond" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "millisecond");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/minute/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/minute/prop-desc.js
new file mode 100644
index 00000000000..b3220d0ac03
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/minute/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.minute
+description: The "minute" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "minute");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/month/calendar-returns-infinity.js b/test/built-ins/Temporal/PlainDateTime/prototype/month/calendar-returns-infinity.js
new file mode 100644
index 00000000000..27ec8f94651
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/month/calendar-returns-infinity.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.month
+description: Getter throws if the calendar returns ±∞ from its month method
+features: [Temporal]
+---*/
+
+class InfinityCalendar extends Temporal.Calendar {
+ constructor(positive) {
+ super("iso8601");
+ this.positive = positive;
+ }
+
+ month() {
+ return this.positive ? Infinity : -Infinity;
+ }
+}
+
+const pos = new InfinityCalendar(true);
+const instance1 = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321, pos);
+assert.throws(RangeError, () => instance1.month);
+
+const neg = new InfinityCalendar(false);
+const instance2 = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321, neg);
+assert.throws(RangeError, () => instance2.month);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/month/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/month/prop-desc.js
new file mode 100644
index 00000000000..5b8fa49c3d1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/month/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.month
+description: The "month" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "month");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/monthCode/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/monthCode/prop-desc.js
new file mode 100644
index 00000000000..84cc3faf320
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/monthCode/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.monthcode
+description: The "monthCode" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "monthCode");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/prop-desc.js
new file mode 100644
index 00000000000..846e23362c2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/monthsInYear/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.monthsinyear
+description: The "monthsInYear" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "monthsInYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/nanosecond/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/nanosecond/prop-desc.js
new file mode 100644
index 00000000000..33c4d78e24f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/nanosecond/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.nanosecond
+description: The "nanosecond" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "nanosecond");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/round/builtin.js b/test/built-ins/Temporal/PlainDateTime/prototype/round/builtin.js
new file mode 100644
index 00000000000..cb595773855
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/round/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: >
+ Tests that Temporal.PlainDateTime.prototype.round
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.round),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.round),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.round),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.round.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/round/length.js b/test/built-ins/Temporal/PlainDateTime/prototype/round/length.js
new file mode 100644
index 00000000000..96e445c6c47
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/round/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Temporal.PlainDateTime.prototype.round.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.round, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/round/name.js b/test/built-ins/Temporal/PlainDateTime/prototype/round/name.js
new file mode 100644
index 00000000000..64b915023c0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/round/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Temporal.PlainDateTime.prototype.round.name is "round".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.round, "name", {
+ value: "round",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/round/not-a-constructor.js b/test/built-ins/Temporal/PlainDateTime/prototype/round/not-a-constructor.js
new file mode 100644
index 00000000000..ec7f0178681
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/round/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: >
+ Temporal.PlainDateTime.prototype.round does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.round();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.round), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.round)");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/round/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/round/prop-desc.js
new file mode 100644
index 00000000000..1b444a3b7de
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/round/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: The "round" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.round,
+ "function",
+ "`typeof PlainDateTime.prototype.round` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "round", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-nan.js b/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-nan.js
new file mode 100644
index 00000000000..79c19123a0a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-nan.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal-totemporaldatetimeroundingincrement step 5:
+ 5. Return ? ToTemporalRoundingIncrement(_normalizedOptions_, _maximum_, *false*).
+ sec-temporal.plaindatetime.prototype.round step 8:
+ 8. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_options_, _smallestUnit_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+assert.throws(RangeError, () => datetime.round({ smallestUnit: 'second', roundingIncrement: NaN }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-non-integer.js b/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-non-integer.js
new file mode 100644
index 00000000000..baa035eb76f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Rounding for roundingIncrement option
+info: |
+ sec-temporal-totemporalroundingincrement step 7:
+ 7. Set _increment_ to floor(ℝ(_increment_)).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 5);
+const result = datetime.round({ smallestUnit: "nanosecond", roundingIncrement: 2.5 });
+TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 0, 0, 6, "roundingIncrement 2.5 floors to 2");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-out-of-range.js b/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-out-of-range.js
new file mode 100644
index 00000000000..f35ed2e16be
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-out-of-range.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ sec-temporal-totemporalroundingincrement step 6:
+ 6. If _increment_ < 1 or _increment_ > _maximum_, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 5);
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: -1 }));
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: 0 }));
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: Infinity }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-undefined.js
new file mode 100644
index 00000000000..2cd1595f952
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-undefined.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal-totemporaldatetimeroundingincrement step 5:
+ 5. Return ? ToTemporalRoundingIncrement(_normalizedOptions_, _maximum_, *false*).
+ sec-temporal.plaindatetime.prototype.round step 8:
+ 8. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_options_, _smallestUnit_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const explicit = datetime.round({ smallestUnit: 'second', roundingIncrement: undefined });
+TemporalHelpers.assertPlainDateTime(explicit, 2000, 5, "M05", 2, 12, 34, 57, 0, 0, 0, "default roundingIncrement is 1");
+
+const implicit = datetime.round({ smallestUnit: 'second' });
+TemporalHelpers.assertPlainDateTime(implicit, 2000, 5, "M05", 2, 12, 34, 57, 0, 0, 0, "default roundingIncrement is 1");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-wrong-type.js
new file mode 100644
index 00000000000..3833832c3a2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingincrement-wrong-type.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.round
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal-totemporaldatetimeroundingincrement step 5:
+ 5. Return ? ToTemporalRoundingIncrement(_normalizedOptions_, _maximum_, *false*).
+ sec-temporal.plaindatetime.prototype.round step 8:
+ 8. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_options_, _smallestUnit_).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => datetime.round({ smallestUnit: 'second', roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 57, 0, 0, 0, descr),
+ (result, descr) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 0, 0, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-invalid-string.js b/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-invalid-string.js
new file mode 100644
index 00000000000..00a3140c539
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-invalid-string.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "microsecond", roundingMode: "other string" }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-undefined.js
new file mode 100644
index 00000000000..b3166f554d7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-undefined.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const explicit1 = datetime.round({ smallestUnit: "microsecond", roundingMode: undefined });
+TemporalHelpers.assertPlainDateTime(explicit1, 2000, 5, "M05", 2, 12, 34, 56, 123, 988, 0, "default roundingMode is halfExpand");
+const implicit1 = datetime.round({ smallestUnit: "microsecond" });
+TemporalHelpers.assertPlainDateTime(implicit1, 2000, 5, "M05", 2, 12, 34, 56, 123, 988, 0, "default roundingMode is halfExpand");
+
+const explicit2 = datetime.round({ smallestUnit: "millisecond", roundingMode: undefined });
+TemporalHelpers.assertPlainDateTime(explicit2, 2000, 5, "M05", 2, 12, 34, 56, 124, 0, 0, "default roundingMode is halfExpand");
+const implicit2 = datetime.round({ smallestUnit: "millisecond" });
+TemporalHelpers.assertPlainDateTime(implicit2, 2000, 5, "M05", 2, 12, 34, 56, 124, 0, 0, "default roundingMode is halfExpand");
+
+const explicit3 = datetime.round({ smallestUnit: "second", roundingMode: undefined });
+TemporalHelpers.assertPlainDateTime(explicit3, 2000, 5, "M05", 2, 12, 34, 56, 0, 0, 0, "default roundingMode is halfExpand");
+const implicit3 = datetime.round({ smallestUnit: "second" });
+TemporalHelpers.assertPlainDateTime(implicit3, 2000, 5, "M05", 2, 12, 34, 56, 0, 0, 0, "default roundingMode is halfExpand");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-wrong-type.js
new file mode 100644
index 00000000000..05c28592371
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/round/roundingmode-wrong-type.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "halfExpand",
+ (roundingMode) => datetime.round({ smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 123, 988, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-invalid-string.js b/test/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-invalid-string.js
new file mode 100644
index 00000000000..e4d8f8503c1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-invalid-string.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-plurals-accepted.js b/test/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-plurals-accepted.js
new file mode 100644
index 00000000000..c80362d92d1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-plurals-accepted.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 789, 999, 999);
+const validUnits = [
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => datetime.round({ smallestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-wrong-type.js
new file mode 100644
index 00000000000..aa0a13c5445
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/round/smallestunit-wrong-type.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => datetime.round({ smallestUnit }),
+ (result, descr) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 123, 988, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/round/subclassing-ignored.js b/test/built-ins/Temporal/PlainDateTime/prototype/round/subclassing-ignored.js
new file mode 100644
index 00000000000..1b2e0227fab
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/round/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.round
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDateTime,
+ [2000, 5, 2, 12, 34, 56, 987, 654, 321],
+ "round",
+ [{ smallestUnit: 'second' }],
+ (result) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 57, 0, 0, 0),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/second/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/second/prop-desc.js
new file mode 100644
index 00000000000..493e38407c6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/second/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.second
+description: The "second" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "second");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/argument-plaindate.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/argument-plaindate.js
new file mode 100644
index 00000000000..ab84703fe5a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/argument-plaindate.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Fast path for converting Temporal.PlainDate to Temporal.PlainDateTime by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.b:
+ b. If _item_ has an [[InitializedTemporalDate]] internal slot, then
+ i. Return ? CreateTemporalDateTime(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], 0, 0, 0, 0, 0, 0, _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalPlainDateTimeFastPath((date, calendar) => {
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 0, 0, 0, 987, 654, 321, calendar);
+ const result = datetime.since(date);
+ assert.sameValue(result.total({ unit: "nanoseconds" }), 987654321, "PlainDate is converted to midnight");
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-balance-negative-time-units.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 00000000000..cd633b338d1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Negative time fields are balanced upwards if the argument is given as ZonedDateTime
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-totemporaldatetime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaindatetime.prototype.since step 3:
+ 3. Set _other_ ? ToTemporalDateTime(_other_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// exact time's epoch parts
+const tz = new Temporal.TimeZone("-00:00:00.000000002");
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+const diff = new Temporal.PlainDateTime(1970, 1, 1).since(datetime);
+
+TemporalHelpers.assertDuration(diff, 0, 0, 0, 0, -1, -1, -1, -1, 0, -999);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-negative-epochnanoseconds.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 00000000000..3ee4053650b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+const result = instance.since(datetime);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 11239, 22, 40, 10, 987, 654, 320);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..a0696a0ca4a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.since(zoned));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..ed77eab137b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.since(zoned));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..450e21cd587
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => plain.since(zoned));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/balance-negative-duration.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/balance-negative-duration.js
new file mode 100644
index 00000000000..d11948b6519
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/balance-negative-duration.js
@@ -0,0 +1,34 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Negative durations are balanced correctly by the modulo operation in NanosecondsToDays
+info: |
+ sec-temporal-nanosecondstodays step 6:
+ 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
+ sec-temporal-balanceduration step 4:
+ 4. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_).
+ sec-temporal-differenceisodatetime steps 7 and 13:
+ 7. If _timeSign_ is -_dateSign_, then
+ ...
+ b. Set _timeDifference_ to ? BalanceDuration(-_timeSign_, _timeDifference_.[[Hours]], _timeDifference_.[[Minutes]], _timeDifference_.[[Seconds]], _timeDifference_.[[Milliseconds]], _timeDifference_.[[Microseconds]], _timeDifference_.[[Nanoseconds]], _largestUnit_).
+ ...
+ 16. Return ? BalanceDuration(_dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], _dateDifference_.[[Days]], _timeDifference_.[[Hours]], _timeDifference_.[[Minutes]], _timeDifference_.[[Seconds]], _timeDifference_.[[Milliseconds]], _timeDifference_.[[Microseconds]], _timeDifference_.[[Nanoseconds]], _largestUnit_).
+ sec-temporal.plaindatetime.prototype.since step 14:
+ 14. Let _diff_ be ? DifferenceISODateTime(_other_.[[ISOYear]], _other_.[[ISOMonth]], _other_.[[ISODay]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], _largestUnit_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier1 = new Temporal.PlainDateTime(2000, 5, 2, 9);
+const later1 = new Temporal.PlainDateTime(2000, 5, 5, 10);
+const result1 = later1.since(earlier1, { largestUnit: 'day' });
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 3, 1, 0, 0, 0, 0, 0, "date sign == time sign");
+
+const earlier2 = new Temporal.PlainDateTime(2000, 5, 2, 10);
+const later2 = new Temporal.PlainDateTime(2000, 5, 5, 9);
+const result2 = later2.since(earlier2, { largestUnit: 'day' });
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 2, 23, 0, 0, 0, 0, 0, "date sign != time sign");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/balance-negative-time-units.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/balance-negative-time-units.js
new file mode 100644
index 00000000000..a5fcdba9456
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/balance-negative-time-units.js
@@ -0,0 +1,50 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-differencetime step 8:
+ 8. Let _bt_ be ? BalanceTime(_hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal-differenceisodatetime step 2:
+ 2. Let _timeDifference_ be ? DifferenceTime(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_).
+ sec-temporal.plaindatetime.prototype.since step 14:
+ 14. Let _diff_ be ? DifferenceISODateTime(_other_.[[ISOYear]], _other_.[[ISOMonth]], _other_.[[ISODay]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], _largestUnit_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1996, 5, 2, 1, 1, 1, 1, 1, 1);
+
+const result1 = datetime.since(new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = datetime.since(new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 0, 0, 2));
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = datetime.since(new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 0, 2));
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = datetime.since(new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 2));
+TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = datetime.since(new Temporal.PlainDateTime(1996, 5, 2, 0, 2));
+TemporalHelpers.assertDuration(result5, 0, 0, 0, 0, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+// This one is different because hours are later balanced again in BalanceDuration
+const result6 = datetime.since(new Temporal.PlainDateTime(1996, 5, 2, 2));
+TemporalHelpers.assertDuration(result6, 0, 0, 0, 0, 0, -58, -58, -998, -998, -999, "hours balance");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/builtin.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/builtin.js
new file mode 100644
index 00000000000..157d7f532e1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: >
+ Tests that Temporal.PlainDateTime.prototype.since
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.since),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.since),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.since),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.since.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-copy-of-options.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-copy-of-options.js
new file mode 100644
index 00000000000..82cbddc3b5a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-copy-of-options.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: The dateUntil() method on the calendar is called with a copy of the options bag
+features: [Temporal]
+---*/
+
+const originalOptions = {
+ largestUnit: "year",
+ shouldBeCopied: {},
+};
+let called = false;
+
+class Calendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateUntil(d1, d2, options) {
+ called = true;
+ assert.notSameValue(options, originalOptions, "options bag should be a copy");
+ assert.sameValue(options.shouldBeCopied, originalOptions.shouldBeCopied, "options bag should be a shallow copy");
+ return new Temporal.Duration();
+ }
+}
+const calendar = new Calendar();
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322, calendar);
+earlier.since(later, originalOptions);
+assert(called, "calendar.dateUntil must be called");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-plaindate-calendar.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-plaindate-calendar.js
new file mode 100644
index 00000000000..69f335a7235
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-plaindate-calendar.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: calendar.dateUntil() is passed PlainDate objects with the receiver's calendar
+info: |
+ DifferenceISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2, d2, h2, min2, s2, ms2, mus2, ns2, calendar, largestUnit [ , options ] )
+
+ 8. Let _date1_ be ? CreateTemporalDate(_balanceResult_.[[Year]], _balanceResult_.[[Month]], _balanceResult_.[[Day]], _calendar_).
+ 9. Let _date2_ be ? CreateTemporalDate(_y2_, _mon2_, _d2_, _calendar_).
+ 12. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+features: [Temporal]
+---*/
+
+class Calendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateUntil(d1, d2) {
+ assert.sameValue(d1.calendar, this, "d1.calendar");
+ assert.sameValue(d2.calendar, this, "d2.calendar");
+ return new Temporal.Duration();
+ }
+}
+const calendar = new Calendar();
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322, calendar);
+const result = earlier.since(later);
+assert(result instanceof Temporal.Duration, "result");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 00000000000..b6f71c759da
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.plaindatetime.prototype.since step 14:
+ 14. Let _diff_ be ? DifferenceISODateTime(_other_.[[ISOYear]], _other_.[[ISOMonth]], _other_.[[ISODay]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], _largestUnit_, _options_).
+ sec-temporal-differenceisodatetime steps 9–11:
+ 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
+ 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
+ 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+ const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322, calendar);
+ later.since(earlier, { largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"],
+ weeks: ["week"],
+ days: ["day"],
+ hours: ["day"],
+ minutes: ["day"],
+ seconds: ["day"],
+ milliseconds: ["day"],
+ microseconds: ["day"],
+ nanoseconds: ["day"]
+ }
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/calendar-fields-iterable.js
new file mode 100644
index 00000000000..1a6308d8416
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/calendar-fields-iterable.js
@@ -0,0 +1,39 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "hour",
+ "microsecond",
+ "millisecond",
+ "minute",
+ "month",
+ "monthCode",
+ "nanosecond",
+ "second",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+datetime.since({ year: 2005, month: 6, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/calendar-temporal-object.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/calendar-temporal-object.js
new file mode 100644
index 00000000000..791434c927f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, temporalObject);
+ datetime.since({ year: 2005, month: 6, day: 2, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..37cbba98eae
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.prototype.since
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15);
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.since({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.since({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-invalid-string.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-invalid-string.js
new file mode 100644
index 00000000000..d20696dfc11
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 987, 654, 321);
+assert.throws(RangeError, () => later.since(earlier, { largestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-plurals-accepted.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-plurals-accepted.js
new file mode 100644
index 00000000000..2aebc4901f3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-plurals-accepted.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 12, 13, 35, 57, 988, 655, 322);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => later.since(earlier, { largestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-undefined.js
new file mode 100644
index 00000000000..db8b37c1cc7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 987, 654, 321);
+
+const explicit = later.since(earlier, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 397, 1, 1, 1, 987, 654, 321, "default largestUnit is day");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 397, 1, 1, 1, 987, 654, 321, "default largestUnit is day");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-wrong-type.js
new file mode 100644
index 00000000000..dc30a217eb4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/largestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 987, 654, 321);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "year",
+ (largestUnit) => later.since(earlier, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 1, 0, 1, 1, 1, 1, 987, 654, 321, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/length.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/length.js
new file mode 100644
index 00000000000..c181ef281e8
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Temporal.PlainDateTime.prototype.since.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.since, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/name.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/name.js
new file mode 100644
index 00000000000..7c6e2a4f284
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Temporal.PlainDateTime.prototype.since.name is "since".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.since, "name", {
+ value: "since",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/not-a-constructor.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/not-a-constructor.js
new file mode 100644
index 00000000000..f1e848f523e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: >
+ Temporal.PlainDateTime.prototype.since does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.since();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.since), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.since)");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/options-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/options-undefined.js
new file mode 100644
index 00000000000..9eb9db1ae2e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/options-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2000, 6, 12, 12, 34, 56, 987, 654, 322);
+
+const explicit = later.since(earlier, undefined);
+assert.sameValue(explicit.years, 0, "default largest unit is days");
+assert.sameValue(explicit.months, 0, "default largest unit is days");
+assert.sameValue(explicit.weeks, 0, "default largest unit is days");
+assert.sameValue(explicit.days, 41, "default largest unit is days");
+assert.sameValue(explicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
+
+const implicit = later.since(earlier);
+assert.sameValue(implicit.years, 0, "default largest unit is days");
+assert.sameValue(implicit.months, 0, "default largest unit is days");
+assert.sameValue(implicit.weeks, 0, "default largest unit is days");
+assert.sameValue(implicit.days, 41, "default largest unit is days");
+assert.sameValue(implicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/prop-desc.js
new file mode 100644
index 00000000000..610f0884bfd
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: The "since" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.since,
+ "function",
+ "`typeof PlainDateTime.prototype.since` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "since", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/read-time-fields-before-datefromfields.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/read-time-fields-before-datefromfields.js
new file mode 100644
index 00000000000..9f7acc26d6a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/read-time-fields-before-datefromfields.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.e:
+ e. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = new Temporal.PlainDateTime(2021, 3, 31, 12, 34, 56, 987, 654, 321);
+const duration = datetime.since({ year: 2021, month: 3, day: 31, calendar });
+
+TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, 12, 34, 56, 987, 654, 321);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/round-negative-duration.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/round-negative-duration.js
new file mode 100644
index 00000000000..0383746037b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/round-negative-duration.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Negative durations are rounded correctly by the modulo operation in NanosecondsToDays
+info: |
+ sec-temporal-nanosecondstodays step 6:
+ 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
+ sec-temporal-roundduration step 6:
+ 6. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
+ sec-temporal.plaindatetime.prototype.since step 15:
+ 15. Let _roundResult_ be ? RoundDuration(−_diff_.[[Years]], −_diff_.[[Months]], −_diff_.[[Weeks]], −_diff_.[[Days]], −_diff_.[[Hours]], −_diff_.[[Minutes]], −_diff_.[[Seconds]], −_diff_.[[Milliseconds]], −_diff_.[[Microseconds]], −_diff_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _dateTime_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12);
+const later = new Temporal.PlainDateTime(2000, 5, 5);
+const result = later.since(earlier, { smallestUnit: "day", roundingIncrement: 2 });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-nan.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-nan.js
new file mode 100644
index 00000000000..355067c2462
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-nan.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindatetime.prototype.since step 13:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322);
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: NaN }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-non-integer.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-non-integer.js
new file mode 100644
index 00000000000..05265ab0b10
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-non-integer.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Rounding for roundingIncrement option
+info: |
+ sec-temporal-totemporalroundingincrement step 7:
+ 7. Set _increment_ to floor(ℝ(_increment_)).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 5);
+const result = later.since(earlier, { roundingIncrement: 2.5 });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, "roundingIncrement 2.5 floors to 2");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-out-of-range.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-out-of-range.js
new file mode 100644
index 00000000000..b274fab67a4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-out-of-range.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ sec-temporal-totemporalroundingincrement step 6:
+ 6. If _increment_ < 1 or _increment_ > _maximum_, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 5);
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: Infinity }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-undefined.js
new file mode 100644
index 00000000000..a11d4730000
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindatetime.prototype.since step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322);
+
+const explicit = later.since(earlier, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 397, 1, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 397, 1, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-wrong-type.js
new file mode 100644
index 00000000000..87a695a7427
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingincrement-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindatetime.prototype.since step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => later.since(earlier, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 397, 1, 1, 1, 1, 1, 1, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 397, 1, 1, 1, 1, 1, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-invalid-string.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-invalid-string.js
new file mode 100644
index 00000000000..59b1314d566
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 123, 987, 500);
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "microsecond", roundingMode: "other string" }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-undefined.js
new file mode 100644
index 00000000000..f23689b2d93
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-undefined.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 123, 987, 500);
+
+const explicit1 = later.since(earlier, { smallestUnit: "microsecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 1, 1, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+const implicit1 = later.since(earlier, { smallestUnit: "microsecond" });
+TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 1, 1, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+
+const explicit2 = later.since(earlier, { smallestUnit: "millisecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 1, 1, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+const implicit2 = later.since(earlier, { smallestUnit: "millisecond" });
+TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 1, 1, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+
+const explicit3 = later.since(earlier, { smallestUnit: "second", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit3, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, "default roundingMode is trunc");
+const implicit3 = later.since(earlier, { smallestUnit: "second" });
+TemporalHelpers.assertDuration(implicit3, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, "default roundingMode is trunc");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-wrong-type.js
new file mode 100644
index 00000000000..e74e381aedc
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => later.since(earlier, { smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 1, 1, 1, 1, 123, 987, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-invalid-string.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-invalid-string.js
new file mode 100644
index 00000000000..94d4409a3ba
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 987, 654, 321);
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-plurals-accepted.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-plurals-accepted.js
new file mode 100644
index 00000000000..191fc9dbf52
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-plurals-accepted.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 12, 13, 35, 57, 988, 655, 322);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => later.since(earlier, { smallestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-undefined.js
new file mode 100644
index 00000000000..8d0b34f9635
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 987, 654, 321);
+
+const explicit = later.since(earlier, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 1, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 1, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-wrong-type.js
new file mode 100644
index 00000000000..47841f1edeb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/smallestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.since
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 987, 654, 321);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => later.since(earlier, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 1, 1, 1, 1, 987, 654, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-not-object.js b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-not-object.js
new file mode 100644
index 00000000000..b60b50dcfdb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-not-object.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Passing a primitive other than string to subtract() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+assert.throws(RangeError, () => instance.subtract(undefined), "undefined");
+assert.throws(RangeError, () => instance.subtract(null), "null");
+assert.throws(RangeError, () => instance.subtract(true), "boolean");
+assert.throws(RangeError, () => instance.subtract(""), "empty string");
+assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol");
+assert.throws(RangeError, () => instance.subtract(7), "number");
+assert.throws(RangeError, () => instance.subtract(7n), "bigint");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string-negative-fractional-units.js b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string-negative-fractional-units.js
new file mode 100644
index 00000000000..2add82f7df7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string-negative-fractional-units.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+
+const resultHours = instance.subtract("-PT24.567890123H");
+TemporalHelpers.assertPlainDateTime(resultHours, 2000, 5, "M05", 3, 0, 34, 4, 404, 442, 799, "negative fractional hours");
+
+const resultMinutes = instance.subtract("-PT1440.567890123M");
+TemporalHelpers.assertPlainDateTime(resultMinutes, 2000, 5, "M05", 3, 0, 0, 34, 73, 407, 379, "negative fractional minutes");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string.js b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string.js
new file mode 100644
index 00000000000..fccc02a2f6e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/argument-string.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+const result = instance.subtract("P3D");
+TemporalHelpers.assertPlainDateTime(result, 2000, 4, "M04", 29, 0, 34, 56, 987, 654, 321);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/subtract/balance-negative-time-units.js b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/balance-negative-time-units.js
new file mode 100644
index 00000000000..e6567581e2c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/balance-negative-time-units.js
@@ -0,0 +1,49 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-addtime step 8:
+ 8. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-adddatetime step 1:
+ 1. Let _timeResult_ be ? AddTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal.plaindatetime.prototype.subtract step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], −_duration_.[[Years]], −_duration_.[[Months]], −_duration_.[[Weeks]], −_duration_.[[Days]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]], _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1996, 5, 2, 1, 1, 1, 1, 1, 1);
+
+const result1 = datetime.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainDateTime(result1, 1996, 5, "M05", 2, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = datetime.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainDateTime(result2, 1996, 5, "M05", 2, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = datetime.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainDateTime(result3, 1996, 5, "M05", 2, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = datetime.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainDateTime(result4, 1996, 5, "M05", 2, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = datetime.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainDateTime(result5, 1996, 5, "M05", 2, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+const result6 = datetime.subtract(new Temporal.Duration(0, 0, 0, 0, 2));
+TemporalHelpers.assertPlainDateTime(result6, 1996, 5, "M05", 1, 23, 1, 1, 1, 1, 1, "hours balance");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/subtract/builtin.js b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/builtin.js
new file mode 100644
index 00000000000..f9a1a9d5fa0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: >
+ Tests that Temporal.PlainDateTime.prototype.subtract
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.subtract),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.subtract),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.subtract),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.subtract.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/subtract/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..733f04dddca
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/infinity-throws-rangeerror.js
@@ -0,0 +1,35 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDateTime.prototype.subtract throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.plaindatetime.prototype.subtract
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/subtract/length.js b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/length.js
new file mode 100644
index 00000000000..93a46ecebb2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Temporal.PlainDateTime.prototype.subtract.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.subtract, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/subtract/name.js b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/name.js
new file mode 100644
index 00000000000..58b0020aa27
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Temporal.PlainDateTime.prototype.subtract.name is "subtract".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.subtract, "name", {
+ value: "subtract",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/subtract/negative-infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/negative-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..a5bc9593cb0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,35 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainDateTime.prototype.subtract throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.plaindatetime.prototype.subtract
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: -Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/subtract/non-integer-throws-rangeerror.js b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/non-integer-throws-rangeerror.js
new file mode 100644
index 00000000000..50ba0bdd596
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/non-integer-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.subtract({ [field]: -1.5 }));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/subtract/not-a-constructor.js b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/not-a-constructor.js
new file mode 100644
index 00000000000..21e31a90ca1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: >
+ Temporal.PlainDateTime.prototype.subtract does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.subtract();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.subtract), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.subtract)");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/subtract/options-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/options-undefined.js
new file mode 100644
index 00000000000..04dd700532a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/options-undefined.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 3, 31, 12, 34, 56, 987, 654, 321);
+const duration = { months: 1 };
+
+const explicit = datetime.subtract(duration, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = datetime.subtract(duration);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/subtract/order-of-operations.js b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/order-of-operations.js
new file mode 100644
index 00000000000..0294d1bf773
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/order-of-operations.js
@@ -0,0 +1,74 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Properties on an object passed to subtract() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const expected = [
+ "get days",
+ "get days.valueOf",
+ "call days.valueOf",
+ "get hours",
+ "get hours.valueOf",
+ "call hours.valueOf",
+ "get microseconds",
+ "get microseconds.valueOf",
+ "call microseconds.valueOf",
+ "get milliseconds",
+ "get milliseconds.valueOf",
+ "call milliseconds.valueOf",
+ "get minutes",
+ "get minutes.valueOf",
+ "call minutes.valueOf",
+ "get months",
+ "get months.valueOf",
+ "call months.valueOf",
+ "get nanoseconds",
+ "get nanoseconds.valueOf",
+ "call nanoseconds.valueOf",
+ "get seconds",
+ "get seconds.valueOf",
+ "call seconds.valueOf",
+ "get weeks",
+ "get weeks.valueOf",
+ "call weeks.valueOf",
+ "get years",
+ "get years.valueOf",
+ "call years.valueOf",
+];
+const actual = [];
+const fields = {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = instance.subtract(argument);
+TemporalHelpers.assertPlainDateTime(result, 1999, 3, "M03", 25, 11, 33, 55, 986, 653, 320);
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-invalid-string.js b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-invalid-string.js
new file mode 100644
index 00000000000..1e1f1761c81
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-invalid-string.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-adddatetime step 4:
+ 4. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.plaindatetime.prototype.subtract step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], −_duration_.[[Years]], −_duration_.[[Months]], −_duration_.[[Weeks]], −_duration_.[[Days]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]], _options_).
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDateTime(2000, 5, 2, 12);
+const duration = new Temporal.Duration(3, 3, 0, 3, 3);
+assert.throws(RangeError, () => date.subtract(duration, { overflow: "other string" }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-undefined.js
new file mode 100644
index 00000000000..ccdce7624bd
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-undefined.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.subtract
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-adddatetime step 4:
+ 4. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.plaindatetime.prototype.subtract step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], −_duration_.[[Years]], −_duration_.[[Months]], −_duration_.[[Weeks]], −_duration_.[[Days]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]], _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 31, 12);
+const duration = new Temporal.Duration(3, 1);
+
+const explicit = datetime.subtract(duration, { overflow: undefined });
+TemporalHelpers.assertPlainDateTime(explicit, 1997, 4, "M04", 30, 12, 0, 0, 0, 0, 0, "default overflow is constrain");
+const implicit = datetime.subtract(duration, {});
+TemporalHelpers.assertPlainDateTime(implicit, 1997, 4, "M04", 30, 12, 0, 0, 0, 0, 0, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-wrong-type.js
new file mode 100644
index 00000000000..d536cd10f8e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/overflow-wrong-type.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-adddatetime step 4:
+ 4. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.plaindatetime.prototype.subtract step 5:
+ 5. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]], −_duration_.[[Years]], −_duration_.[[Months]], −_duration_.[[Weeks]], −_duration_.[[Days]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]], _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12);
+const duration = new Temporal.Duration(3, 3, 0, 3, 3);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => datetime.subtract(duration, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainDateTime(result, 1997, 1, "M01", 30, 9, 0, 0, 0, 0, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/subtract/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/prop-desc.js
new file mode 100644
index 00000000000..40162788fc8
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: The "subtract" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.subtract,
+ "function",
+ "`typeof PlainDateTime.prototype.subtract` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "subtract", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/subtract/subclassing-ignored.js b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/subclassing-ignored.js
new file mode 100644
index 00000000000..657278f1cef
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.subtract
+description: Objects of a subclass are never created as return values for subtract()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDateTime,
+ [2000, 5, 2, 12, 34, 56, 987, 654, 321],
+ "subtract",
+ [{ nanoseconds: 1 }],
+ (result) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 320),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toJSON/builtin.js b/test/built-ins/Temporal/PlainDateTime/prototype/toJSON/builtin.js
new file mode 100644
index 00000000000..fb31ff82735
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toJSON/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tojson
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toJSON
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toJSON/length.js b/test/built-ins/Temporal/PlainDateTime/prototype/toJSON/length.js
new file mode 100644
index 00000000000..cea157e23b5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toJSON/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tojson
+description: Temporal.PlainDateTime.prototype.toJSON.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toJSON/name.js b/test/built-ins/Temporal/PlainDateTime/prototype/toJSON/name.js
new file mode 100644
index 00000000000..3c809d52755
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toJSON/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tojson
+description: Temporal.PlainDateTime.prototype.toJSON.name is "toJSON".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toJSON/not-a-constructor.js b/test/built-ins/Temporal/PlainDateTime/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 00000000000..f752138da64
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toJSON/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tojson
+description: >
+ Temporal.PlainDateTime.prototype.toJSON does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toJSON), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toJSON)");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toJSON/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/toJSON/prop-desc.js
new file mode 100644
index 00000000000..019d407eba3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toJSON/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tojson
+description: The "toJSON" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toJSON,
+ "function",
+ "`typeof PlainDateTime.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/builtin.js b/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/builtin.js
new file mode 100644
index 00000000000..d8828b711e8
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tolocalestring
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toLocaleString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.toLocaleString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toLocaleString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toLocaleString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toLocaleString.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/length.js b/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/length.js
new file mode 100644
index 00000000000..ed2fdc15603
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tolocalestring
+description: Temporal.PlainDateTime.prototype.toLocaleString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toLocaleString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/locales-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/locales-undefined.js
new file mode 100644
index 00000000000..83e27595509
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/locales-undefined.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tolocalestring
+description: Omitting the locales argument defaults to the DateTimeFormat default
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const defaultFormatter = new Intl.DateTimeFormat([], Object.create(null));
+const expected = defaultFormatter.format(datetime);
+
+const actualExplicit = datetime.toLocaleString(undefined);
+assert.sameValue(actualExplicit, expected, "default locale is determined by Intl.DateTimeFormat");
+
+const actualImplicit = datetime.toLocaleString();
+assert.sameValue(actualImplicit, expected, "default locale is determined by Intl.DateTimeFormat");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/name.js b/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/name.js
new file mode 100644
index 00000000000..f6fbed25d3a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tolocalestring
+description: Temporal.PlainDateTime.prototype.toLocaleString.name is "toLocaleString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toLocaleString, "name", {
+ value: "toLocaleString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/not-a-constructor.js b/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/not-a-constructor.js
new file mode 100644
index 00000000000..a60c1fa0e5a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tolocalestring
+description: >
+ Temporal.PlainDateTime.prototype.toLocaleString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.toLocaleString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toLocaleString), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toLocaleString)");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/options-conflict.js b/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/options-conflict.js
new file mode 100644
index 00000000000..13fefadde6c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/options-conflict.js
@@ -0,0 +1,47 @@
+// Copyright (C) 2021 Kate Miháliková. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sup-temporal.plaindatetime.prototype.tolocalestring
+description: >
+ Conflicting properties of dateStyle must be rejected with a TypeError for the options argument
+info: |
+ Using sec-temporal-getdatetimeformatpattern:
+ GetDateTimeFormatPattern ( dateStyle, timeStyle, matcher, opt, dataLocaleData, hc )
+
+ 1. If dateStyle is not undefined or timeStyle is not undefined, then
+ a. For each row in Table 7, except the header row, do
+ i. Let prop be the name given in the Property column of the row.
+ ii. Let p be opt.[[]].
+ iii. If p is not undefined, then
+ 1. Throw a TypeError exception.
+features: [Temporal]
+---*/
+
+// Table 14 - Supported fields + example value for each field
+const conflictingOptions = [
+ [ "weekday", "short" ],
+ [ "era", "short" ],
+ [ "year", "numeric" ],
+ [ "month", "numeric" ],
+ [ "day", "numeric" ],
+ [ "hour", "numeric" ],
+ [ "minute", "numeric" ],
+ [ "second", "numeric" ],
+ [ "dayPeriod", "short" ],
+ [ "fractionalSecondDigits", 3 ],
+];
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+assert.sameValue(typeof datetime.toLocaleString("en", { dateStyle: "short" }), "string");
+assert.sameValue(typeof datetime.toLocaleString("en", { timeStyle: "short" }), "string");
+
+for (const [ option, value ] of conflictingOptions) {
+ assert.throws(TypeError, function() {
+ datetime.toLocaleString("en", { [option]: value, dateStyle: "short" });
+ }, `datetime.toLocaleString("en", { ${option}: "${value}", dateStyle: "short" }) throws TypeError`);
+
+ assert.throws(TypeError, function() {
+ datetime.toLocaleString("en", { [option]: value, timeStyle: "short" });
+ }, `datetime.toLocaleString("en", { ${option}: "${value}", timeStyle: "short" }) throws TypeError`);
+}
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/options-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/options-undefined.js
new file mode 100644
index 00000000000..4bd7be65885
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/options-undefined.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tolocalestring
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const defaultFormatter = new Intl.DateTimeFormat('en', Object.create(null));
+const expected = defaultFormatter.format(datetime);
+
+const actualExplicit = datetime.toLocaleString('en', undefined);
+assert.sameValue(actualExplicit, expected, "default locale is determined by Intl.DateTimeFormat");
+
+const actualImplicit = datetime.toLocaleString('en');
+assert.sameValue(actualImplicit, expected, "default locale is determined by Intl.DateTimeFormat");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/prop-desc.js
new file mode 100644
index 00000000000..a282771d277
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toLocaleString/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tolocalestring
+description: The "toLocaleString" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toLocaleString,
+ "function",
+ "`typeof PlainDateTime.prototype.toLocaleString` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toLocaleString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/builtin.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/builtin.js
new file mode 100644
index 00000000000..3d5d0bbdbcf
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaindate
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toPlainDate
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.toPlainDate),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toPlainDate),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toPlainDate),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toPlainDate.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/length.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/length.js
new file mode 100644
index 00000000000..d401351fa47
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaindate
+description: Temporal.PlainDateTime.prototype.toPlainDate.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toPlainDate, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/name.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/name.js
new file mode 100644
index 00000000000..39e5cf5c2fc
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaindate
+description: Temporal.PlainDateTime.prototype.toPlainDate.name is "toPlainDate".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toPlainDate, "name", {
+ value: "toPlainDate",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/not-a-constructor.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/not-a-constructor.js
new file mode 100644
index 00000000000..f125a5f875f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaindate
+description: >
+ Temporal.PlainDateTime.prototype.toPlainDate does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.toPlainDate();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toPlainDate), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toPlainDate)");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/prop-desc.js
new file mode 100644
index 00000000000..c027480014e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainDate/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaindate
+description: The "toPlainDate" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toPlainDate,
+ "function",
+ "`typeof PlainDateTime.prototype.toPlainDate` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toPlainDate", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin.js
new file mode 100644
index 00000000000..b6e62de3f39
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainmonthday
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toPlainMonthDay
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.toPlainMonthDay),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toPlainMonthDay),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toPlainMonthDay),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toPlainMonthDay.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-arguments.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-arguments.js
new file mode 100644
index 00000000000..37f9efc51d3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-arguments.js
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainmonthday
+description: Correct options value is passed to calendar method
+info: |
+ MonthDayFromFields ( calendar, fields [ , options ] )
+
+ 3. If options is not present, then
+ a. Set options to undefined.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthDayFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.sameValue(args[1], undefined, "args[1]");
+ return super.monthDayFromFields(...args);
+ }
+}
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 456, 789, new CustomCalendar());
+const result = plainDateTime.toPlainMonthDay();
+TemporalHelpers.assertPlainMonthDay(result, "M05", 2);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-fields-iterable.js
new file mode 100644
index 00000000000..64c669a6e6e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/calendar-fields-iterable.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainmonthday
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.prototype.toplainmonthday step 4:
+ 4. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"monthCode"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "monthCode",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+datetime.toPlainMonthDay();
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/length.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/length.js
new file mode 100644
index 00000000000..c7103be3742
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainmonthday
+description: Temporal.PlainDateTime.prototype.toPlainMonthDay.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toPlainMonthDay, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/name.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/name.js
new file mode 100644
index 00000000000..56376fae65c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainmonthday
+description: Temporal.PlainDateTime.prototype.toPlainMonthDay.name is "toPlainMonthDay".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toPlainMonthDay, "name", {
+ value: "toPlainMonthDay",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/not-a-constructor.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/not-a-constructor.js
new file mode 100644
index 00000000000..3b6963660fb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainmonthday
+description: >
+ Temporal.PlainDateTime.prototype.toPlainMonthDay does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.toPlainMonthDay();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toPlainMonthDay), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toPlainMonthDay)");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/prop-desc.js
new file mode 100644
index 00000000000..cfdc4a721d1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainMonthDay/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainmonthday
+description: The "toPlainMonthDay" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toPlainMonthDay,
+ "function",
+ "`typeof PlainDateTime.prototype.toPlainMonthDay` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toPlainMonthDay", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/builtin.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/builtin.js
new file mode 100644
index 00000000000..21b5ce1f7ea
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaintime
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toPlainTime
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.toPlainTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toPlainTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toPlainTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toPlainTime.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/length.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/length.js
new file mode 100644
index 00000000000..c36f1e1d1d2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaintime
+description: Temporal.PlainDateTime.prototype.toPlainTime.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toPlainTime, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/name.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/name.js
new file mode 100644
index 00000000000..ec8dc0f01ac
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaintime
+description: Temporal.PlainDateTime.prototype.toPlainTime.name is "toPlainTime".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toPlainTime, "name", {
+ value: "toPlainTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/not-a-constructor.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/not-a-constructor.js
new file mode 100644
index 00000000000..886328fe52d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaintime
+description: >
+ Temporal.PlainDateTime.prototype.toPlainTime does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.toPlainTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toPlainTime), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toPlainTime)");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/prop-desc.js
new file mode 100644
index 00000000000..c5f1028e4ff
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainTime/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplaintime
+description: The "toPlainTime" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toPlainTime,
+ "function",
+ "`typeof PlainDateTime.prototype.toPlainTime` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toPlainTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin.js
new file mode 100644
index 00000000000..c3a23c1a8a0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainyearmonth
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toPlainYearMonth
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.toPlainYearMonth),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toPlainYearMonth),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toPlainYearMonth),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toPlainYearMonth.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-arguments.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-arguments.js
new file mode 100644
index 00000000000..2b92786f6b4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-arguments.js
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainyearmonth
+description: Correct options value is passed to calendar method
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 3. If options is not present, then
+ a. Set options to undefined.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.sameValue(args[1], undefined, "args[1]");
+ return super.yearMonthFromFields(...args);
+ }
+}
+const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 456, 789, new CustomCalendar());
+const result = plainDateTime.toPlainYearMonth();
+TemporalHelpers.assertPlainYearMonth(result, 2000, 5, "M05");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-fields-iterable.js
new file mode 100644
index 00000000000..3f0157e3b78
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/calendar-fields-iterable.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainyearmonth
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.prototype.toplainyearmonth step 4:
+ 4. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+datetime.toPlainYearMonth();
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/length.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/length.js
new file mode 100644
index 00000000000..f5e656ddd5b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainyearmonth
+description: Temporal.PlainDateTime.prototype.toPlainYearMonth.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toPlainYearMonth, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/name.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/name.js
new file mode 100644
index 00000000000..5daacaf0cd4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainyearmonth
+description: Temporal.PlainDateTime.prototype.toPlainYearMonth.name is "toPlainYearMonth".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toPlainYearMonth, "name", {
+ value: "toPlainYearMonth",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/not-a-constructor.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/not-a-constructor.js
new file mode 100644
index 00000000000..0296188dfd9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainyearmonth
+description: >
+ Temporal.PlainDateTime.prototype.toPlainYearMonth does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.toPlainYearMonth();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toPlainYearMonth), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toPlainYearMonth)");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/prop-desc.js
new file mode 100644
index 00000000000..78d1e3ec682
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toPlainYearMonth/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.toplainyearmonth
+description: The "toPlainYearMonth" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toPlainYearMonth,
+ "function",
+ "`typeof PlainDateTime.prototype.toPlainYearMonth` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toPlainYearMonth", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/builtin.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/builtin.js
new file mode 100644
index 00000000000..7e2b679bda4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-invalid-string.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-invalid-string.js
new file mode 100644
index 00000000000..985ad225ac7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-invalid-string.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.protoype.tostring
+description: RangeError thrown when calendarName option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « String », « *"auto"*, *"always"*, *"never"* », *"auto"*).
+ sec-temporal.plaindatetime.protoype.tostring step 6:
+ 6. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+assert.throws(RangeError, () => datetime.toString({ calendarName: "other string" }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-undefined.js
new file mode 100644
index 00000000000..b8b741b65f2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-undefined.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.protoype.tostring
+description: Fallback value for calendarName option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « String », « *"auto"*, *"always"*, *"never"* », *"auto"*).
+ sec-temporal.plaindatetime.protoype.tostring step 6:
+ 6. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const calendar = {
+ toString() { return "custom"; }
+};
+const datetime1 = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const datetime2 = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+
+[
+ [datetime1, "2000-05-02T12:34:56.987654321"],
+ [datetime2, "2000-05-02T12:34:56.987654321[u-ca=custom]"],
+].forEach(([datetime, expected]) => {
+ const explicit = datetime.toString({ calendarName: undefined });
+ assert.sameValue(explicit, expected, "default calendarName option is auto");
+
+ // See options-undefined.js for {}
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-wrong-type.js
new file mode 100644
index 00000000000..171546b2120
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-wrong-type.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.protoype.tostring
+description: Type conversions for calendarName option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « String », « *"auto"*, *"always"*, *"never"* », *"auto"*).
+ sec-temporal.plaindatetime.protoype.tostring step 6:
+ 6. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = {
+ toString() { return "custom"; }
+};
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+
+TemporalHelpers.checkStringOptionWrongType("calendarName", "auto",
+ (calendarName) => datetime.toString({ calendarName }),
+ (result, descr) => assert.sameValue(result, "2000-05-02T12:34:56.987654321[u-ca=custom]", descr),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-invalid-string.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-invalid-string.js
new file mode 100644
index 00000000000..150d868658a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-invalid-string.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option not one of the allowed string values
+info: |
+ sec-getstringornumberoption step 4:
+ 4. If _stringValues_ is not *undefined* and _stringValues_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaindatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToDurationSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0);
+
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: "other string" }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-nan.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-nan.js
new file mode 100644
index 00000000000..4ea34d8aef2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-nan.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaindatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0);
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: NaN }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-non-integer.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-non-integer.js
new file mode 100644
index 00000000000..7de58c30b34
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-non-integer.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Rounding for fractionalSecondDigits option
+info: |
+ sec-getstringornumberoption step 3.b:
+ b. Return floor(ℝ(_value_)).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaindatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToDurationSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0);
+
+const string = datetime.toString({ fractionalSecondDigits: 2.5 });
+assert.sameValue(string, "2000-05-02T12:34:56.98", "fractionalSecondDigits 2.5 floors to 2");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-out-of-range.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-out-of-range.js
new file mode 100644
index 00000000000..948d707eb35
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-out-of-range.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option out of range
+info: |
+ sec-getstringornumberoption step 3.a:
+ a. If _value_ < _minimum_ or _value_ > _maximum_, throw a *RangeError* exception.
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaindatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToDurationSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0);
+
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: -1 }));
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: 10 }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-undefined.js
new file mode 100644
index 00000000000..12a0f77b873
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-undefined.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Fallback value for fractionalSecondDigits option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, *"stringOrNumber"*, *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaindatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToDurationSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0);
+
+const explicit = datetime.toString({ fractionalSecondDigits: undefined });
+assert.sameValue(explicit, "2000-05-02T12:34:56.98765", "default fractionalSecondDigits is auto");
+
+// See options-undefined.js for {}
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-wrong-type.js
new file mode 100644
index 00000000000..9177b82196a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/fractionalseconddigits-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Type conversions for fractionalSecondDigits option
+info: |
+ sec-getoption steps 8–9:
+ 8. Else if _type_ is Number, then
+ a. Set _value_ to ? ToNumber(value).
+ b. ...
+ 9. Else,
+ a. Set _value_ to ? ToString(value).
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.plaindatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0);
+TemporalHelpers.checkFractionalSecondDigitsOptionWrongType(datetime);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/length.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/length.js
new file mode 100644
index 00000000000..f54fee10a25
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Temporal.PlainDateTime.prototype.toString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/name.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/name.js
new file mode 100644
index 00000000000..43795582771
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Temporal.PlainDateTime.prototype.toString.name is "toString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/not-a-constructor.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/not-a-constructor.js
new file mode 100644
index 00000000000..8e983be0034
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: >
+ Temporal.PlainDateTime.prototype.toString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toString), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toString)");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/options-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/options-undefined.js
new file mode 100644
index 00000000000..0f19b561d29
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/options-undefined.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const calendar = {
+ toString() { return "custom"; }
+};
+const datetime1 = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0);
+const datetime2 = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 650, 0, calendar);
+
+[
+ [datetime1, "2000-05-02T12:34:56.98765"],
+ [datetime2, "2000-05-02T12:34:56.98765[u-ca=custom]"],
+].forEach(([datetime, expected]) => {
+ const explicit = datetime.toString(undefined);
+ assert.sameValue(explicit, expected, "default calendarName option is auto, precision is auto, and no rounding");
+
+ const propertyImplicit = datetime.toString({});
+ assert.sameValue(propertyImplicit, expected, "default calendarName option is auto, precision is auto, and no rounding");
+
+ const implicit = datetime.toString();
+ assert.sameValue(implicit, expected, "default calendarName option is auto, precision is auto, and no rounding");
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/prop-desc.js
new file mode 100644
index 00000000000..c9c04ed4991
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: The "toString" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toString,
+ "function",
+ "`typeof PlainDateTime.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-invalid-string.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-invalid-string.js
new file mode 100644
index 00000000000..3a3c9af8e8f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-invalid-string.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+assert.throws(RangeError, () => datetime.toString({ smallestUnit: "microsecond", roundingMode: "other string" }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-undefined.js
new file mode 100644
index 00000000000..d858b9e0123
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Fallback value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const explicit1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: undefined });
+assert.sameValue(explicit1, "2000-05-02T12:34:56.123987", "default roundingMode is trunc");
+const implicit1 = datetime.toString({ smallestUnit: "microsecond" });
+assert.sameValue(implicit1, "2000-05-02T12:34:56.123987", "default roundingMode is trunc");
+
+const explicit2 = datetime.toString({ smallestUnit: "millisecond", roundingMode: undefined });
+assert.sameValue(explicit2, "2000-05-02T12:34:56.123", "default roundingMode is trunc");
+const implicit2 = datetime.toString({ smallestUnit: "millisecond" });
+assert.sameValue(implicit2, "2000-05-02T12:34:56.123", "default roundingMode is trunc");
+
+const explicit3 = datetime.toString({ smallestUnit: "second", roundingMode: undefined });
+assert.sameValue(explicit3, "2000-05-02T12:34:56", "default roundingMode is trunc");
+const implicit3 = datetime.toString({ smallestUnit: "second" });
+assert.sameValue(implicit3, "2000-05-02T12:34:56", "default roundingMode is trunc");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-wrong-type.js
new file mode 100644
index 00000000000..4d728daeb7b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/roundingmode-wrong-type.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => datetime.toString({ smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => assert.sameValue(result, "2000-05-02T12:34:56.123987", descr),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-invalid-string.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-invalid-string.js
new file mode 100644
index 00000000000..1d1a6aaeca2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-invalid-string.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+assert.throws(RangeError, () => datetime.toString({ smallestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-plurals-accepted.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-plurals-accepted.js
new file mode 100644
index 00000000000..ae13516a604
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-plurals-accepted.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 789, 999, 999);
+const validUnits = [
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => datetime.toString({ smallestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-undefined.js
new file mode 100644
index 00000000000..7b65d002f5f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-undefined.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Fallback value for smallestUnit option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+
+const explicit1 = datetime.toString({ smallestUnit: undefined, fractionalSecondDigits: 6 });
+assert.sameValue(explicit1, "2000-05-02T12:34:56.123987", "default smallestUnit defers to fractionalSecondDigits");
+const implicit1 = datetime.toString({ fractionalSecondDigits: 6 });
+assert.sameValue(implicit1, "2000-05-02T12:34:56.123987", "default smallestUnit defers to fractionalSecondDigits");
+
+const explicit2 = datetime.toString({ smallestUnit: undefined, fractionalSecondDigits: 3 });
+assert.sameValue(explicit2, "2000-05-02T12:34:56.123", "default smallestUnit defers to fractionalSecondDigits");
+const implicit2 = datetime.toString({ fractionalSecondDigits: 3 });
+assert.sameValue(implicit2, "2000-05-02T12:34:56.123", "default smallestUnit defers to fractionalSecondDigits");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-valid-units.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-valid-units.js
new file mode 100644
index 00000000000..34928a9c6d9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-valid-units.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Valid units for the smallestUnit option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 789, 999, 999);
+
+assert.sameValue(datetime.toString({ smallestUnit: "minute" }), "2000-05-02T12:34");
+assert.sameValue(datetime.toString({ smallestUnit: "second" }), "2000-05-02T12:34:56");
+assert.sameValue(datetime.toString({ smallestUnit: "millisecond" }), "2000-05-02T12:34:56.789");
+assert.sameValue(datetime.toString({ smallestUnit: "microsecond" }), "2000-05-02T12:34:56.789999");
+assert.sameValue(datetime.toString({ smallestUnit: "nanosecond" }), "2000-05-02T12:34:56.789999999");
+
+const notValid = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+];
+
+notValid.forEach((smallestUnit) => {
+ assert.throws(RangeError, () => datetime.toString({ smallestUnit }), smallestUnit);
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-wrong-type.js
new file mode 100644
index 00000000000..95ade06c465
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-wrong-type.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tostring
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => datetime.toString({ smallestUnit }),
+ (result, descr) => assert.sameValue(result, "2000-05-02T12:34:56.123987", descr),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/balance-negative-time-units.js b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/balance-negative-time-units.js
new file mode 100644
index 00000000000..2a0427c9c90
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/balance-negative-time-units.js
@@ -0,0 +1,45 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-addtime step 8:
+ 8. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-adddatetime step 1:
+ 1. Let _timeResult_ be ? AddTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal-builtintimezonegetinstantfor step 13.a:
+ a. Let _earlier_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], 0, 0, 0, 0, 0, 0, 0, 0, 0, −_nanoseconds_, *"constrain"*).
+ sec-temporal.plaindatetime.prototype.tozoneddatetime step 6:
+ 6. Let _instant_ be BuiltinTimeZoneGetInstantFor(_timeZone_, _dateTime_, _disambiguation_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const shiftInstant = new Temporal.Instant(3661_001_001_001n);
+const tz = TemporalHelpers.oneShiftTimeZone(shiftInstant, 2);
+const datetime = new Temporal.PlainDateTime(1970, 1, 1, 1, 1, 1, 1, 1, 1);
+
+// This code path is encountered if disambiguation is `earlier` and the shift is
+// a spring-forward change
+datetime.toZonedDateTime(tz, { disambiguation: "earlier" });
+
+const expected = [
+ "1970-01-01T01:01:01.001001001",
+ "1970-01-01T01:01:01.001000999",
+];
+assert.compareArray(tz.getPossibleInstantsForCalledWith, expected);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/builtin.js b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/builtin.js
new file mode 100644
index 00000000000..fb66a11d5d7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: >
+ Tests that Temporal.PlainDateTime.prototype.toZonedDateTime
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.toZonedDateTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.toZonedDateTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.toZonedDateTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.toZonedDateTime.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/calendar-dateadd-called-with-options-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/calendar-dateadd-called-with-options-undefined.js
new file mode 100644
index 00000000000..367dd551445
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/calendar-dateadd-called-with-options-undefined.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: >
+ BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the
+ options value
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddUndefinedOptions();
+const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9);
+const instance = new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, 0, calendar);
+
+["earlier", "compatible", "later"].forEach((disambiguation) => {
+ calendar.dateAddCallCount = 0;
+
+ instance.toZonedDateTime(timeZone, { disambiguation });
+ assert.sameValue(calendar.dateAddCallCount, 1, `calling with disambiguation ${disambiguation}`);
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-invalid-string.js b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-invalid-string.js
new file mode 100644
index 00000000000..65eab04c154
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-invalid-string.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: RangeError thrown when disambiguation option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal.plaindatetime.prototype.tozoneddatetime step 5:
+ 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2001, 9, 9, 1, 46, 40, 987, 654, 321);
+const timeZone = new Temporal.TimeZone("UTC");
+assert.throws(RangeError, () => datetime.toZonedDateTime(timeZone, { disambiguation: "other string" }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-undefined.js
new file mode 100644
index 00000000000..9292987c471
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-undefined.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Fallback value for disambiguation option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal.plaindatetime.prototype.tozoneddatetime step 5:
+ 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const springForwardDatetime = new Temporal.PlainDateTime(2000, 4, 2, 2, 30);
+const fallBackDatetime = new Temporal.PlainDateTime(2000, 10, 29, 1, 30);
+
+[
+ [springForwardDatetime, 954671400_000_000_000n],
+ [fallBackDatetime, 972808200_000_000_000n],
+].forEach(([datetime, expected]) => {
+ const explicit = datetime.toZonedDateTime(timeZone, { disambiguation: undefined });
+ assert.sameValue(explicit.epochNanoseconds, expected, "default disambiguation is compatible");
+ const implicit = datetime.toZonedDateTime(timeZone, {});
+ assert.sameValue(implicit.epochNanoseconds, expected, "default disambiguation is compatible");
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-wrong-type.js
new file mode 100644
index 00000000000..bf7d4dbb176
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguation-wrong-type.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Type conversions for disambiguation option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal.plaindatetime.prototype.tozoneddatetime step 5:
+ 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2001, 9, 9, 1, 46, 40, 987, 654, 321);
+const timeZone = new Temporal.TimeZone("UTC");
+TemporalHelpers.checkStringOptionWrongType("disambiguation", "compatible",
+ (disambiguation) => datetime.toZonedDateTime(timeZone, { disambiguation }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_987_654_321n, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/length.js b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/length.js
new file mode 100644
index 00000000000..e6e7f6e44b9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Temporal.PlainDateTime.prototype.toZonedDateTime.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toZonedDateTime, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/name.js b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/name.js
new file mode 100644
index 00000000000..69a3dc0f065
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Temporal.PlainDateTime.prototype.toZonedDateTime.name is "toZonedDateTime".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.toZonedDateTime, "name", {
+ value: "toZonedDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/not-a-constructor.js b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/not-a-constructor.js
new file mode 100644
index 00000000000..799ba476473
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: >
+ Temporal.PlainDateTime.prototype.toZonedDateTime does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.toZonedDateTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.toZonedDateTime), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.toZonedDateTime)");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-undefined.js
new file mode 100644
index 00000000000..a5faffd4674
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/options-undefined.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tozoneddatetime
+includes: [temporalHelpers.js]
+description: Verify that undefined options are handled correctly.
+features: [BigInt, Temporal]
+---*/
+
+const datetimeEarlier = new Temporal.PlainDateTime(2000, 10, 29, 1, 34, 56, 987, 654, 321);
+const datetimeLater = new Temporal.PlainDateTime(2000, 4, 2, 2, 34, 56, 987, 654, 321);
+const timeZone = TemporalHelpers.springForwardFallBackTimeZone();
+
+[
+ [datetimeEarlier, 972808496987654321n],
+ [datetimeLater, 954671696987654321n],
+].forEach(([datetime, expected]) => {
+ const explicit = datetime.toZonedDateTime(timeZone, undefined);
+ assert.sameValue(explicit.epochNanoseconds, expected, "default disambiguation is compatible");
+
+ const implicit = datetime.toZonedDateTime(timeZone);
+ assert.sameValue(implicit.epochNanoseconds, expected, "default disambiguation is compatible");
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-custom-timezone.js b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-custom-timezone.js
new file mode 100644
index 00000000000..22bd7aecef2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-custom-timezone.js
@@ -0,0 +1,66 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: TimeZone.getPossibleInstantsFor called after processing timeZone and options
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "has timeZone.timeZone",
+ "get options.disambiguation",
+ "get disambiguation.toString",
+ "call disambiguation.toString",
+ "get timeZone.getPossibleInstantsFor",
+ "call timeZone.getPossibleInstantsFor",
+];
+
+Object.defineProperty(Temporal.TimeZone, "from", {
+ get() {
+ actual.push("get Temporal.TimeZone.from");
+ return undefined;
+ },
+});
+
+const dateTime = Temporal.PlainDateTime.from("1975-02-02T14:25:36.123456789");
+const instant = Temporal.Instant.fromEpochNanoseconds(-205156799012345679n);
+
+const options = new Proxy({
+ disambiguation: TemporalHelpers.toPrimitiveObserver(actual, "reject", "disambiguation"),
+}, {
+ has(target, property) {
+ actual.push(`has options.${property}`);
+ return property in target;
+ },
+ get(target, property) {
+ actual.push(`get options.${property}`);
+ return target[property];
+ },
+});
+
+const timeZone = new Proxy({
+ getPossibleInstantsFor(dateTimeArg) {
+ actual.push("call timeZone.getPossibleInstantsFor");
+ assert.sameValue(dateTimeArg, dateTime);
+ return [instant];
+ },
+}, {
+ has(target, property) {
+ actual.push(`has timeZone.${property}`);
+ return property in target;
+ },
+ get(target, property) {
+ actual.push(`get timeZone.${property}`);
+ return target[property];
+ },
+});
+
+const result = dateTime.toZonedDateTime(timeZone, options);
+assert.sameValue(result.epochNanoseconds, instant.epochNanoseconds);
+assert.sameValue(result.timeZone, timeZone);
+assert.sameValue(result.calendar, dateTime.calendar);
+
+assert.compareArray(actual, expected);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/prop-desc.js
new file mode 100644
index 00000000000..f8aa358ea61
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: The "toZonedDateTime" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.toZonedDateTime,
+ "function",
+ "`typeof PlainDateTime.prototype.toZonedDateTime` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "toZonedDateTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..438ddb16e57
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ assert.throws(RangeError, () => datetime.toZonedDateTime(timeZone));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..6a07e4ee272
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ assert.throws(RangeError, () => datetime.toZonedDateTime(timeZone));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..6fe383a115e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ assert.throws(TypeError, () => datetime.toZonedDateTime(timeZone));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 00000000000..e07be8d7d39
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,42 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.plaindatetime.prototype.tozoneddatetime step 6:
+ 6. Let _instant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _temporalDateTime_, *"compatible"*).
+ sec-temporal-builtintimezonegetinstantfor step 1:
+ 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-builtintimezonegetinstantfor step 14:
+ 14. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ sec-temporal-builtintimezonegetinstantfor step 16:
+ 16. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _later_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "2000-05-02T12:34:56.987654321",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ datetime.toZonedDateTime(timeZone);
+}, expected1);
+
+// Same, but test the other path where the time doesn't exist and
+// GetPossibleInstantsFor is called again on a later time
+
+const expected2 = [
+ "2030-01-01T00:30:00",
+ "2030-01-01T01:30:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.PlainDateTime(2030, 1, 1, 0, 30);
+ datetime.toZonedDateTime(timeZone);
+}, expected2);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-datetime.js b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-datetime.js
new file mode 100644
index 00000000000..e6d2ad94ec5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-datetime.js
@@ -0,0 +1,44 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.toZonedDateTime(timeZone), "bare date-time string is not a time zone");
+assert.throws(RangeError, () => instance.toZonedDateTime({ timeZone }), "bare date-time string is not a time zone");
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result1.timeZone.id, "UTC", "date-time + Z is UTC time zone");
+const result2 = instance.toZonedDateTime({ timeZone });
+assert.sameValue(result2.timeZone.id, "UTC", "date-time + Z is UTC time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result3 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result3.timeZone.id, "-07:00", "date-time + offset is the offset time zone");
+const result4 = instance.toZonedDateTime({ timeZone });
+assert.sameValue(result4.timeZone.id, "-07:00", "date-time + offset is the offset time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30[America/Vancouver]";
+const result5 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result5.timeZone.id, "America/Vancouver", "date-time + IANA annotation is the IANA time zone");
+const result6 = instance.toZonedDateTime({ timeZone });
+assert.sameValue(result6.timeZone.id, "America/Vancouver", "date-time + IANA annotation is the IANA time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30Z[America/Vancouver]";
+const result7 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result7.timeZone.id, "America/Vancouver", "date-time + Z + IANA annotation is the IANA time zone");
+const result8 = instance.toZonedDateTime({ timeZone });
+assert.sameValue(result8.timeZone.id, "America/Vancouver", "date-time + Z + IANA annotation is the IANA time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30-07:00[America/Vancouver]";
+const result9 = instance.toZonedDateTime(timeZone);
+assert.sameValue(result9.timeZone.id, "America/Vancouver", "date-time + offset + IANA annotation is the IANA time zone");
+const result10 = instance.toZonedDateTime({ timeZone });
+assert.sameValue(result10.timeZone.id, "America/Vancouver", "date-time + offset + IANA annotation is the IANA time zone (string in property bag)");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/argument-plaindate.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/argument-plaindate.js
new file mode 100644
index 00000000000..be3e5dc0683
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/argument-plaindate.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Fast path for converting Temporal.PlainDate to Temporal.PlainDateTime by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.b:
+ b. If _item_ has an [[InitializedTemporalDate]] internal slot, then
+ i. Return ? CreateTemporalDateTime(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], 0, 0, 0, 0, 0, 0, _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalPlainDateTimeFastPath((date, calendar) => {
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 0, 0, 0, 987, 654, 321, calendar);
+ const result = datetime.until(date);
+ assert.sameValue(result.total({ unit: "nanoseconds" }), -987654321, "PlainDate is converted to midnight");
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-balance-negative-time-units.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 00000000000..3353d9960a4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Negative time fields are balanced upwards if the argument is given as ZonedDateTime
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-totemporaldatetime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaindatetime.prototype.until step 3:
+ 3. Set _other_ ? ToTemporalDateTime(_other_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// exact time's epoch parts
+const tz = new Temporal.TimeZone("-00:00:00.000000002");
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+const diff = new Temporal.PlainDateTime(1970, 1, 1).until(datetime);
+
+TemporalHelpers.assertDuration(diff, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-negative-epochnanoseconds.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 00000000000..43f32818f3a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+const result = instance.until(datetime);
+TemporalHelpers.assertDuration(result, 0, 0, 0, -11239, -22, -40, -10, -987, -654, -320);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..01d9f07c85f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.until(zoned));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..bbefa4ee11b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.until(zoned));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..eeeb0003869
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => plain.until(zoned));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/balance-negative-duration.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/balance-negative-duration.js
new file mode 100644
index 00000000000..2de57f305e5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/balance-negative-duration.js
@@ -0,0 +1,34 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Negative durations are balanced correctly by the modulo operation in NanosecondsToDays
+info: |
+ sec-temporal-nanosecondstodays step 6:
+ 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
+ sec-temporal-balanceduration step 4:
+ 4. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_).
+ sec-temporal-differenceisodatetime steps 7 and 13:
+ 7. If _timeSign_ is -_dateSign_, then
+ ...
+ b. Set _timeDifference_ to ? BalanceDuration(-_timeSign_, _timeDifference_.[[Hours]], _timeDifference_.[[Minutes]], _timeDifference_.[[Seconds]], _timeDifference_.[[Milliseconds]], _timeDifference_.[[Microseconds]], _timeDifference_.[[Nanoseconds]], _largestUnit_).
+ ...
+ 13. Return ? BalanceDuration(_dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], _dateDifference_.[[Days]], _timeDifference_.[[Hours]], _timeDifference_.[[Minutes]], _timeDifference_.[[Seconds]], _timeDifference_.[[Milliseconds]], _timeDifference_.[[Microseconds]], _timeDifference_.[[Nanoseconds]], _largestUnit_).
+ sec-temporal.plaindatetime.prototype.until step 13:
+ 13. Let _diff_ be ? DifferenceISODateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _other_.[[ISOYear]], _other_.[[ISOMonth]], _other_.[[ISODay]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _dateTime_.[[Calendar]], _largestUnit_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier1 = new Temporal.PlainDateTime(2000, 5, 2, 9);
+const later1 = new Temporal.PlainDateTime(2000, 5, 5, 10);
+const result1 = later1.until(earlier1, { largestUnit: 'day' });
+TemporalHelpers.assertDuration(result1, 0, 0, 0, -3, -1, 0, 0, 0, 0, 0, "date sign == time sign");
+
+const earlier2 = new Temporal.PlainDateTime(2000, 5, 2, 10);
+const later2 = new Temporal.PlainDateTime(2000, 5, 5, 9);
+const result2 = later2.until(earlier2, { largestUnit: 'day' });
+TemporalHelpers.assertDuration(result2, 0, 0, 0, -2, -23, 0, 0, 0, 0, 0, "date sign != time sign");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/balance-negative-time-units.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/balance-negative-time-units.js
new file mode 100644
index 00000000000..150837788aa
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/balance-negative-time-units.js
@@ -0,0 +1,50 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-differencetime step 8:
+ 8. Let _bt_ be ? BalanceTime(_hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal-differenceisodatetime step 2:
+ 2. Let _timeDifference_ be ? DifferenceTime(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_).
+ sec-temporal.plaindatetime.prototype.until step 13:
+ 13. Let _diff_ be ? DifferenceISODateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _other_.[[ISOYear]], _other_.[[ISOMonth]], _other_.[[ISODay]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _dateTime_.[[Calendar]], _largestUnit_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(1996, 5, 2, 1, 1, 1, 1, 1, 1);
+
+const result1 = new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 0, 0, 0, 2).until(datetime);
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 0, 0, 2).until(datetime);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 0, 2).until(datetime);
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = new Temporal.PlainDateTime(1996, 5, 2, 0, 0, 2).until(datetime);
+TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = new Temporal.PlainDateTime(1996, 5, 2, 0, 2).until(datetime);
+TemporalHelpers.assertDuration(result5, 0, 0, 0, 0, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+// This one is different because hours are later balanced again in BalanceDuration
+const result6 = new Temporal.PlainDateTime(1996, 5, 2, 2).until(datetime);
+TemporalHelpers.assertDuration(result6, 0, 0, 0, 0, 0, -58, -58, -998, -998, -999, "hours balance");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/balance.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/balance.js
new file mode 100644
index 00000000000..08687951c4e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/balance.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Results with opposite-sign components (e.g. months=1, hours=-1) are balanced correctly
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const a = Temporal.PlainDateTime.from('2017-10-05T08:07:14+00:00[UTC]');
+const b = Temporal.PlainDateTime.from('2021-03-05T03:32:45+00:00[UTC]');
+const c = Temporal.PlainDateTime.from('2021-03-05T09:32:45+00:00[UTC]');
+
+const r1 = a.until(b, { largestUnit: 'months' });
+TemporalHelpers.assertDuration(r1, 0, 40, 0, 27, 19, 25, 31, 0, 0, 0, "r1");
+assert.sameValue(a.add(r1).toString(), b.toString(), "a.add(r1)");
+
+const r2 = b.until(a, { largestUnit: 'months' });
+TemporalHelpers.assertDuration(r2, 0, -40, 0, -30, -19, -25, -31, 0, 0, 0, "r2");
+assert.sameValue(b.add(r2).toString(), a.toString(), "b.add(r2)");
+
+const r3 = c.until(a, { largestUnit: 'months' });
+TemporalHelpers.assertDuration(r3, 0, -41, 0, 0, -1, -25, -31, 0, 0, 0, "r3");
+assert.sameValue(c.add(r3).toString(), a.toString(), "c.add(r3)");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/builtin.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/builtin.js
new file mode 100644
index 00000000000..eb7715a412e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: >
+ Tests that Temporal.PlainDateTime.prototype.until
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.until),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.until),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.until),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.until.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-copy-of-options.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-copy-of-options.js
new file mode 100644
index 00000000000..3805a5ece4c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-copy-of-options.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: The dateUntil() method on the calendar is called with a copy of the options bag
+features: [Temporal]
+---*/
+
+const originalOptions = {
+ largestUnit: "year",
+ shouldBeCopied: {},
+};
+let called = false;
+
+class Calendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateUntil(d1, d2, options) {
+ called = true;
+ assert.notSameValue(options, originalOptions, "options bag should be a copy");
+ assert.sameValue(options.shouldBeCopied, originalOptions.shouldBeCopied, "options bag should be a shallow copy");
+ return new Temporal.Duration();
+ }
+}
+const calendar = new Calendar();
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322, calendar);
+earlier.until(later, originalOptions);
+assert(called, "calendar.dateUntil must be called");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-plaindate-calendar.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-plaindate-calendar.js
new file mode 100644
index 00000000000..7f255df452d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-plaindate-calendar.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: calendar.dateUntil() is passed PlainDate objects with the receiver's calendar
+info: |
+ DifferenceISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2, d2, h2, min2, s2, ms2, mus2, ns2, calendar, largestUnit [ , options ] )
+
+ 8. Let _date1_ be ? CreateTemporalDate(_balanceResult_.[[Year]], _balanceResult_.[[Month]], _balanceResult_.[[Day]], _calendar_).
+ 9. Let _date2_ be ? CreateTemporalDate(_y2_, _mon2_, _d2_, _calendar_).
+ 12. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+features: [Temporal]
+---*/
+
+class Calendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateUntil(d1, d2) {
+ assert.sameValue(d1.calendar, this, "d1.calendar");
+ assert.sameValue(d2.calendar, this, "d2.calendar");
+ return new Temporal.Duration();
+ }
+}
+const calendar = new Calendar();
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322, calendar);
+const result = earlier.until(later);
+assert(result instanceof Temporal.Duration, "result");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 00000000000..c3e0447a15c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.plaindatetime.prototype.until step 13:
+ 13. Let _diff_ be ? DifferenceISODateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _other_.[[ISOYear]], _other_.[[ISOMonth]], _other_.[[ISODay]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _dateTime_.[[Calendar]], _largestUnit_, _options_).
+ sec-temporal-differenceisodatetime steps 9–11:
+ 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
+ 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
+ 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+ const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322, calendar);
+ earlier.until(later, { largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"],
+ weeks: ["week"],
+ days: ["day"],
+ hours: ["day"],
+ minutes: ["day"],
+ seconds: ["day"],
+ milliseconds: ["day"],
+ microseconds: ["day"],
+ nanoseconds: ["day"]
+ }
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/calendar-fields-iterable.js
new file mode 100644
index 00000000000..cbad88be3f2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/calendar-fields-iterable.js
@@ -0,0 +1,39 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "hour",
+ "microsecond",
+ "millisecond",
+ "minute",
+ "month",
+ "monthCode",
+ "nanosecond",
+ "second",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+datetime.until({ year: 2005, month: 6, day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/calendar-temporal-object.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/calendar-temporal-object.js
new file mode 100644
index 00000000000..f3fd0477f2d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const date = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, temporalObject);
+ date.until({ year: 2005, month: 6, day: 2, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..b11bccf398f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.prototype.until
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15);
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.until({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.until({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-invalid-string.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-invalid-string.js
new file mode 100644
index 00000000000..9f0b525b1a2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 987, 654, 321);
+assert.throws(RangeError, () => earlier.until(later, { largestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-plurals-accepted.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-plurals-accepted.js
new file mode 100644
index 00000000000..0e4c7e41514
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-plurals-accepted.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 12, 13, 35, 57, 988, 655, 322);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => earlier.until(later, { largestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-undefined.js
new file mode 100644
index 00000000000..32b872dc31e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 987, 654, 321);
+
+const explicit = earlier.until(later, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 397, 1, 1, 1, 987, 654, 321, "default largestUnit is day");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 397, 1, 1, 1, 987, 654, 321, "default largestUnit is day");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-wrong-type.js
new file mode 100644
index 00000000000..318dd1d7452
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/largestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 987, 654, 321);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "year",
+ (largestUnit) => earlier.until(later, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 1, 0, 1, 1, 1, 1, 987, 654, 321, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/length.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/length.js
new file mode 100644
index 00000000000..4509da11b9b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Temporal.PlainDateTime.prototype.until.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.until, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/name.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/name.js
new file mode 100644
index 00000000000..699af9c91ff
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Temporal.PlainDateTime.prototype.until.name is "until".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.until, "name", {
+ value: "until",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/not-a-constructor.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/not-a-constructor.js
new file mode 100644
index 00000000000..c5c80f01562
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: >
+ Temporal.PlainDateTime.prototype.until does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.until();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.until), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.until)");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/options-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/options-undefined.js
new file mode 100644
index 00000000000..b3cd4d74035
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/options-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2000, 6, 12, 12, 34, 56, 987, 654, 322);
+
+const explicit = earlier.until(later, undefined);
+assert.sameValue(explicit.years, 0, "default largest unit is days");
+assert.sameValue(explicit.months, 0, "default largest unit is days");
+assert.sameValue(explicit.weeks, 0, "default largest unit is days");
+assert.sameValue(explicit.days, 41, "default largest unit is days");
+assert.sameValue(explicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
+
+const implicit = earlier.until(later);
+assert.sameValue(implicit.years, 0, "default largest unit is days");
+assert.sameValue(implicit.months, 0, "default largest unit is days");
+assert.sameValue(implicit.weeks, 0, "default largest unit is days");
+assert.sameValue(implicit.days, 41, "default largest unit is days");
+assert.sameValue(implicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/prop-desc.js
new file mode 100644
index 00000000000..b5cb2e18789
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: The "until" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.until,
+ "function",
+ "`typeof PlainDateTime.prototype.until` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "until", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/read-time-fields-before-datefromfields.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/read-time-fields-before-datefromfields.js
new file mode 100644
index 00000000000..bcd805bfc6a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/read-time-fields-before-datefromfields.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalDateTime(_other_).
+ sec-temporal-totemporaldatetime step 2.e:
+ e. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = new Temporal.PlainDateTime(2021, 3, 31, 12, 34, 56, 987, 654, 321);
+const duration = datetime.until({ year: 2021, month: 3, day: 31, calendar });
+
+TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, -12, -34, -56, -987, -654, -321);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/round-negative-duration.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/round-negative-duration.js
new file mode 100644
index 00000000000..2d6b1971569
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/round-negative-duration.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Negative durations are rounded correctly by the modulo operation in NanosecondsToDays
+info: |
+ sec-temporal-nanosecondstodays step 6:
+ 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
+ sec-temporal-roundduration step 6:
+ 6. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
+ sec-temporal.plaindatetime.prototype.until step 14:
+ 14. Let _roundResult_ be ? RoundDuration(_diff_.[[Years]], _diff_.[[Months]], _diff_.[[Weeks]], _diff_.[[Days]], _diff_.[[Hours]], _diff_.[[Minutes]], _diff_.[[Seconds]], _diff_.[[Milliseconds]], _diff_.[[Microseconds]], _diff_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _dateTime_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12);
+const later = new Temporal.PlainDateTime(2000, 5, 5);
+const result = later.until(earlier, { smallestUnit: "day", roundingIncrement: 2 });
+TemporalHelpers.assertDuration(result, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-nan.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-nan.js
new file mode 100644
index 00000000000..6441976e1de
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-nan.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindatetime.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: NaN }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-non-integer.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-non-integer.js
new file mode 100644
index 00000000000..add5d0fd1ff
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-non-integer.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Rounding for roundingIncrement option
+info: |
+ sec-temporal-totemporalroundingincrement step 7:
+ 7. Set _increment_ to floor(ℝ(_increment_)).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 5);
+const result = earlier.until(later, { roundingIncrement: 2.5 });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, "roundingIncrement 2.5 floors to 2");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-out-of-range.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-out-of-range.js
new file mode 100644
index 00000000000..6fa1d3ee8f1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-out-of-range.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ sec-temporal-totemporalroundingincrement step 6:
+ 6. If _increment_ < 1 or _increment_ > _maximum_, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 5);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: Infinity }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-undefined.js
new file mode 100644
index 00000000000..d9cf9cccf1b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindatetime.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322);
+
+const explicit = earlier.until(later, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 397, 1, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 397, 1, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-wrong-type.js
new file mode 100644
index 00000000000..8d229807e3b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingincrement-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaindatetime.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 3, 13, 35, 57, 988, 655, 322);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => earlier.until(later, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 397, 1, 1, 1, 1, 1, 1, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 397, 1, 1, 1, 1, 1, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-invalid-string.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-invalid-string.js
new file mode 100644
index 00000000000..f907ababfd0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 123, 987, 500);
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "microsecond", roundingMode: "other string" }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-undefined.js
new file mode 100644
index 00000000000..695d40f9c15
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-undefined.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 123, 987, 500);
+
+const explicit1 = earlier.until(later, { smallestUnit: "microsecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 1, 1, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+const implicit1 = earlier.until(later, { smallestUnit: "microsecond" });
+TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 1, 1, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+
+const explicit2 = earlier.until(later, { smallestUnit: "millisecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 1, 1, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+const implicit2 = earlier.until(later, { smallestUnit: "millisecond" });
+TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 1, 1, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+
+const explicit3 = earlier.until(later, { smallestUnit: "second", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit3, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, "default roundingMode is trunc");
+const implicit3 = earlier.until(later, { smallestUnit: "second" });
+TemporalHelpers.assertDuration(implicit3, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, "default roundingMode is trunc");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-wrong-type.js
new file mode 100644
index 00000000000..28c2838ed9f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => earlier.until(later, { smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 1, 1, 1, 1, 123, 987, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-invalid-string.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-invalid-string.js
new file mode 100644
index 00000000000..e009d5b9e64
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 987, 654, 321);
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-plurals-accepted.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-plurals-accepted.js
new file mode 100644
index 00000000000..4b9c89858fb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-plurals-accepted.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainDateTime(2001, 6, 12, 13, 35, 57, 988, 655, 322);
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => earlier.until(later, { smallestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-undefined.js
new file mode 100644
index 00000000000..b707e016083
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 987, 654, 321);
+
+const explicit = earlier.until(later, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 1, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 1, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-wrong-type.js
new file mode 100644
index 00000000000..6cbbdbec1b9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/smallestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainDateTime(2000, 5, 3, 13, 35, 57, 987, 654, 321);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => earlier.until(later, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 1, 1, 1, 1, 987, 654, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/valueOf/builtin.js b/test/built-ins/Temporal/PlainDateTime/prototype/valueOf/builtin.js
new file mode 100644
index 00000000000..5372b910d35
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/valueOf/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.valueof
+description: >
+ Tests that Temporal.PlainDateTime.prototype.valueOf
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.valueOf),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.valueOf),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.valueOf),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.valueOf.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/valueOf/length.js b/test/built-ins/Temporal/PlainDateTime/prototype/valueOf/length.js
new file mode 100644
index 00000000000..a05f558e261
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/valueOf/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.valueof
+description: Temporal.PlainDateTime.prototype.valueOf.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.valueOf, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/valueOf/name.js b/test/built-ins/Temporal/PlainDateTime/prototype/valueOf/name.js
new file mode 100644
index 00000000000..bedc961cff3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/valueOf/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.valueof
+description: Temporal.PlainDateTime.prototype.valueOf.name is "valueOf".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.valueOf, "name", {
+ value: "valueOf",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/valueOf/not-a-constructor.js b/test/built-ins/Temporal/PlainDateTime/prototype/valueOf/not-a-constructor.js
new file mode 100644
index 00000000000..bf7e12fec07
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/valueOf/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.valueof
+description: >
+ Temporal.PlainDateTime.prototype.valueOf does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.valueOf();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.valueOf), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.valueOf)");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/valueOf/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/valueOf/prop-desc.js
new file mode 100644
index 00000000000..78c55221bb7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/valueOf/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.valueof
+description: The "valueOf" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.valueOf,
+ "function",
+ "`typeof PlainDateTime.prototype.valueOf` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "valueOf", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/prop-desc.js
new file mode 100644
index 00000000000..5c1876a0d63
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/weekOfYear/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.weekofyear
+description: The "weekOfYear" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "weekOfYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/with/builtin.js b/test/built-ins/Temporal/PlainDateTime/prototype/with/builtin.js
new file mode 100644
index 00000000000..d0ef5cab360
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/with/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: >
+ Tests that Temporal.PlainDateTime.prototype.with
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.with),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.with),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.with),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.with.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/with/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainDateTime/prototype/with/calendar-fields-iterable.js
new file mode 100644
index 00000000000..1fd4c0a531e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/with/calendar-fields-iterable.js
@@ -0,0 +1,35 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.prototype.with step 9:
+ 9. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "hour",
+ "microsecond",
+ "millisecond",
+ "minute",
+ "month",
+ "monthCode",
+ "nanosecond",
+ "second",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+datetime.with({ year: 2005 });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/with/calendar-merge-fields-returns-primitive.js b/test/built-ins/Temporal/PlainDateTime/prototype/with/calendar-merge-fields-returns-primitive.js
new file mode 100644
index 00000000000..0aed6a31be1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/with/calendar-merge-fields-returns-primitive.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: >
+ with() should throw a TypeError if mergeFields() returns a primitive,
+ without passing the value on to any other calendar methods
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[undefined, null, true, 3.14159, "bad value", Symbol("no"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarMergeFieldsReturnsPrimitive(primitive);
+ const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321, calendar);
+ assert.throws(TypeError, () => instance.with({ year: 2005 }), "bad return from mergeFields() throws");
+ assert.sameValue(calendar.dateFromFieldsCallCount, 0, "dateFromFields() never called");
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/with/copies-merge-fields-object.js b/test/built-ins/Temporal/PlainDateTime/prototype/with/copies-merge-fields-object.js
new file mode 100644
index 00000000000..6b43351aa29
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/with/copies-merge-fields-object.js
@@ -0,0 +1,55 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: The object returned from mergeFields() is copied before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.prototype.with steps 13–15:
+ 13. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialDate_).
+ 14. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, «»).
+ 15. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields step 2:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get day",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get microsecond",
+ "get microsecond.valueOf",
+ "call microsecond.valueOf",
+ "get millisecond",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf",
+ "get minute",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get month",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get nanosecond",
+ "get nanosecond.valueOf",
+ "call nanosecond.valueOf",
+ "get second",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+];
+
+const calendar = TemporalHelpers.calendarMergeFieldsGetters();
+const datetime = new Temporal.PlainDateTime(2021, 3, 31, 12, 34, 56, 987, 654, 321, calendar);
+datetime.with({ year: 2022 });
+
+assert.compareArray(calendar.mergeFieldsReturnOperations, expected, "getters called on mergeFields return");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/with/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDateTime/prototype/with/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..a9075a1c115
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/with/infinity-throws-rangeerror.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.prototype.with
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.with({ [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.with({ [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/with/length.js b/test/built-ins/Temporal/PlainDateTime/prototype/with/length.js
new file mode 100644
index 00000000000..ca7f052ead0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/with/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: Temporal.PlainDateTime.prototype.with.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.with, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/with/name.js b/test/built-ins/Temporal/PlainDateTime/prototype/with/name.js
new file mode 100644
index 00000000000..12e410b5eb3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/with/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: Temporal.PlainDateTime.prototype.with.name is "with".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.with, "name", {
+ value: "with",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/with/not-a-constructor.js b/test/built-ins/Temporal/PlainDateTime/prototype/with/not-a-constructor.js
new file mode 100644
index 00000000000..ac9839ae8cf
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/with/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: >
+ Temporal.PlainDateTime.prototype.with does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.with();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.with), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.with)");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/with/options-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/with/options-undefined.js
new file mode 100644
index 00000000000..62f6ec1b1b2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/with/options-undefined.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 2, 2, 12, 34, 56, 987, 654, 321);
+const fields = { day: 31 };
+
+const explicit = datetime.with(fields, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = datetime.with(fields);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/with/order-of-operations.js b/test/built-ins/Temporal/PlainDateTime/prototype/with/order-of-operations.js
new file mode 100644
index 00000000000..be6e0797b83
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/with/order-of-operations.js
@@ -0,0 +1,76 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: Properties on an object passed to with() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+const expected = [
+ "get calendar",
+ "get timeZone",
+ "get day",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get microsecond",
+ "get microsecond.valueOf",
+ "call microsecond.valueOf",
+ "get millisecond",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf",
+ "get minute",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get month",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get nanosecond",
+ "get nanosecond.valueOf",
+ "call nanosecond.valueOf",
+ "get second",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+];
+const actual = [];
+const fields = {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+ day: 1.7,
+ hour: 1.7,
+ minute: 1.7,
+ second: 1.7,
+ millisecond: 1.7,
+ microsecond: 1.7,
+ nanosecond: 1.7,
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = instance.with(argument);
+TemporalHelpers.assertPlainDateTime(result, 1, 1, "M01", 1, 1, 1, 1, 1, 1, 1);
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/with/overflow-invalid-string.js b/test/built-ins/Temporal/PlainDateTime/prototype/with/overflow-invalid-string.js
new file mode 100644
index 00000000000..9e3f5617846
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/with/overflow-invalid-string.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindatetime.prototype.with step 16:
+ 16. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12);
+assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: "other string" }));
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/with/overflow-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/with/overflow-undefined.js
new file mode 100644
index 00000000000..2602bbb1db6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/with/overflow-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindatetime.prototype.with step 16:
+ 16. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12);
+const explicit = datetime.with({ minute: 67 }, { overflow: undefined });
+TemporalHelpers.assertPlainDateTime(explicit, 2000, 5, "M05", 2, 12, 59, 0, 0, 0, 0, "default overflow is constrain");
+const implicit = datetime.with({ minute: 67 }, {});
+TemporalHelpers.assertPlainDateTime(implicit, 2000, 5, "M05", 2, 12, 59, 0, 0, 0, 0, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/with/overflow-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/with/overflow-wrong-type.js
new file mode 100644
index 00000000000..bc4485bf572
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/with/overflow-wrong-type.js
@@ -0,0 +1,46 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plaindatetime.prototype.with step 16:
+ 16. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12);
+
+// See TemporalHelpers.checkStringOptionWrongType(); this code path has
+// different expectations for observable calls
+
+assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: null }), "null");
+assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: true }), "true");
+assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: false }), "false");
+assert.throws(TypeError, () => datetime.with({ minute: 45 }, { overflow: Symbol() }), "symbol");
+assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: 2n }), "bigint");
+assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: {} }), "plain object");
+
+// toString property is read once by Calendar.dateFromFields() in the builtin
+// calendars, to get the option value for the date part, and then once again
+// internally to get the option value for the time part.
+const expected = [
+ "get overflow.toString",
+ "call overflow.toString",
+ "get overflow.toString",
+ "call overflow.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");
+const result = datetime.with({ minute: 45 }, { overflow: observer });
+TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 45, 0, 0, 0, 0, "object with toString");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/with/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/with/prop-desc.js
new file mode 100644
index 00000000000..899a714e90e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/with/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: The "with" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.with,
+ "function",
+ "`typeof PlainDateTime.prototype.with` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "with", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/with/read-time-fields-before-datefromfields.js b/test/built-ins/Temporal/PlainDateTime/prototype/with/read-time-fields-before-datefromfields.js
new file mode 100644
index 00000000000..e1bfefea9ee
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/with/read-time-fields-before-datefromfields.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.prototype.with step 15:
+ 15. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = new Temporal.PlainDateTime(2021, 3, 31, 12, 34, 56, 987, 654, 321, calendar);
+const newDatetime = datetime.with({ year: 2022 });
+
+assert.sameValue(newDatetime.hour, 12, "hour value");
+assert.sameValue(newDatetime.minute, 34, "minute value");
+assert.sameValue(newDatetime.second, 56, "second value");
+assert.sameValue(newDatetime.millisecond, 987, "millisecond value");
+assert.sameValue(newDatetime.microsecond, 654, "microsecond value");
+assert.sameValue(newDatetime.nanosecond, 321, "nanosecond value");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/with/subclassing-ignored.js b/test/built-ins/Temporal/PlainDateTime/prototype/with/subclassing-ignored.js
new file mode 100644
index 00000000000..3defebda5d3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/with/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: Objects of a subclass are never created as return values for with()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDateTime,
+ [2000, 5, 2, 12, 34, 56, 987, 654, 321],
+ "with",
+ [{ nanosecond: 1 }],
+ (result) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 12, 34, 56, 987, 654, 1),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/builtin.js b/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/builtin.js
new file mode 100644
index 00000000000..01bb94690ff
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: >
+ Tests that Temporal.PlainDateTime.prototype.withCalendar
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.withCalendar),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.withCalendar),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.withCalendar),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.withCalendar.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-temporal-object.js b/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-temporal-object.js
new file mode 100644
index 00000000000..d433a932aad
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/calendar-temporal-object.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.withcalendar step 3:
+ 3. Let _calendar_ be ? ToTemporalCalendar(_calendarLike_).
+ 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 plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const result = plainDateTime.withCalendar(temporalObject);
+ assert.sameValue(result.calendar, calendar, 'Temporal object coerced to calendar');
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/length.js b/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/length.js
new file mode 100644
index 00000000000..6f6c340132c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: Temporal.PlainDateTime.prototype.withCalendar.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.withCalendar, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/missing-argument.js b/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/missing-argument.js
new file mode 100644
index 00000000000..de83f7c9977
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/missing-argument.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: RangeError thrown when calendar argument not given
+features: [Temporal]
+---*/
+
+const plainDateTime = Temporal.PlainDateTime.from("1976-11-18T14:00:00");
+assert.throws(RangeError, () => plainDateTime.withCalendar(), "missing argument");
+assert.throws(RangeError, () => plainDateTime.withCalendar(undefined), "undefined argument");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/name.js b/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/name.js
new file mode 100644
index 00000000000..9f4d18cde27
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: Temporal.PlainDateTime.prototype.withCalendar.name is "withCalendar".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.withCalendar, "name", {
+ value: "withCalendar",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/not-a-constructor.js b/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/not-a-constructor.js
new file mode 100644
index 00000000000..3a872458869
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: >
+ Temporal.PlainDateTime.prototype.withCalendar does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.withCalendar();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.withCalendar), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.withCalendar)");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/prop-desc.js
new file mode 100644
index 00000000000..c5451e7e42d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: The "withCalendar" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.withCalendar,
+ "function",
+ "`typeof PlainDateTime.prototype.withCalendar` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "withCalendar", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/subclassing-ignored.js b/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/subclassing-ignored.js
new file mode 100644
index 00000000000..749bdd6694d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withCalendar/subclassing-ignored.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withcalendar
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const customCalendar = {
+ era() { return undefined; },
+ eraYear() { return undefined; },
+ year() { return 1900; },
+ month() { return 2; },
+ monthCode() { return "M02"; },
+ day() { return 5; },
+ toString() { return "custom-calendar"; },
+};
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDateTime,
+ [2000, 5, 2, 12, 34, 56, 987, 654, 321],
+ "withCalendar",
+ [customCalendar],
+ (result) => {
+ TemporalHelpers.assertPlainDateTime(result, 1900, 2, "M02", 5, 12, 34, 56, 987, 654, 321);
+ assert.sameValue(result.calendar, customCalendar, "calendar result");
+ },
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindatetime.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindatetime.js
new file mode 100644
index 00000000000..04b5b4eea9a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-plaindatetime.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.withplaindate
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.withplaindate step 3:
+ 3. Let _plainDate_ be ? ToTemporalDate(_plainDateLike_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const receiver = new Temporal.PlainDateTime(2001, 9, 9, 6, 54, 32, 123, 456, 789);
+ const result = receiver.withPlainDate(datetime);
+ TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 6, 54, 32, 123, 456, 789);
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..5e509cbd718
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.withPlainDate(zoned));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..c82f3666075
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.withPlainDate(zoned));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..bc40c663542
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => plain.withPlainDate(zoned));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/builtin.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/builtin.js
new file mode 100644
index 00000000000..957ea648543
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: >
+ Tests that Temporal.PlainDateTime.prototype.withPlainDate
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.withPlainDate),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.withPlainDate),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.withPlainDate),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.withPlainDate.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-fields-iterable.js
new file mode 100644
index 00000000000..b0a98cf1bdd
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-fields-iterable.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plaindatetime.prototype.withplaindate step 3:
+ 3. Let _plainDate_ be ? ToTemporalDate(_plainDateLike_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.PlainDateTime(2000, 5, 3, 13, 3, 27, 123, 456, 789, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+datetime.withPlainDate({ year: 2001, month: 6, day: 4, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-temporal-object.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-temporal-object.js
new file mode 100644
index 00000000000..b2c9b06eb1f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/calendar-temporal-object.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.withplaindate step 3:
+ 3. Let _plainDate_ be ? ToTemporalDate(_plainDateLike_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const datetime = new Temporal.PlainDateTime(2000, 5, 3, 13, 3, 27, 123, 456, 789);
+ // the PlainDate's calendar will override the PlainDateTime's ISO calendar
+ const result = datetime.withPlainDate({ year: 2001, month: 6, day: 4, calendar: temporalObject });
+ assert.sameValue(result.calendar, calendar, "Temporal object coerced to calendar");
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..79aabe45736
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.withPlainDate({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.withPlainDate({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/length.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/length.js
new file mode 100644
index 00000000000..0e3a8151cb3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Temporal.PlainDateTime.prototype.withPlainDate.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.withPlainDate, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/name.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/name.js
new file mode 100644
index 00000000000..bd7bba26c69
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Temporal.PlainDateTime.prototype.withPlainDate.name is "withPlainDate".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.withPlainDate, "name", {
+ value: "withPlainDate",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/not-a-constructor.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/not-a-constructor.js
new file mode 100644
index 00000000000..cc14f976285
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: >
+ Temporal.PlainDateTime.prototype.withPlainDate does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.withPlainDate();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.withPlainDate), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.withPlainDate)");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/prop-desc.js
new file mode 100644
index 00000000000..7a2c809043f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: The "withPlainDate" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.withPlainDate,
+ "function",
+ "`typeof PlainDateTime.prototype.withPlainDate` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "withPlainDate", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/subclassing-ignored.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/subclassing-ignored.js
new file mode 100644
index 00000000000..33a31aeef81
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainDate/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDateTime,
+ [2000, 5, 2, 12, 34, 56, 987, 654, 321],
+ "withPlainDate",
+ ["1999-04-27"],
+ (result) => TemporalHelpers.assertPlainDateTime(result, 1999, 4, "M04", 27, 12, 34, 56, 987, 654, 321),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-balance-negative-time-units.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 00000000000..dec1fcd8c9c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,44 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: Negative time fields are balanced upwards if the argument is given as ZonedDateTime
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-totemporaltime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Set _plainDateTime_ to ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaindatetime.prototype.withplaintime step 4:
+ 4. Let _plainTime_ be ? ToTemporalTime(_plainTimeLike_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// exact time's epoch parts
+const tz = new Temporal.TimeZone("-00:00:00.000000002");
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+const pdt = new Temporal.PlainDateTime(2000, 5, 2);
+const newpdt = pdt.withPlainTime(datetime);
+
+TemporalHelpers.assertPlainDateTime(newpdt, 2000, 5, "M05", 2, 1, 1, 1, 1, 0, 999);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-negative-epochnanoseconds.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 00000000000..0a58b635af1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
+const result = instance.withPlainTime(datetime);
+TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 16, 50, 35, 0, 0, 1);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..5698566e0ed
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.withPlainTime(zoned));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..8b5c8a24e03
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => plain.withPlainTime(zoned));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..0e7f04aa76e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const plain = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+ const zoned = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => plain.withPlainTime(zoned));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/builtin.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/builtin.js
new file mode 100644
index 00000000000..edcd698e10f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: >
+ Tests that Temporal.PlainDateTime.prototype.withPlainTime
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainDateTime.prototype.withPlainTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainDateTime.prototype.withPlainTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainDateTime.prototype.withPlainTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainDateTime.prototype.withPlainTime.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/calendar-temporal-object.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/calendar-temporal-object.js
new file mode 100644
index 00000000000..ddf67ab2fe0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/calendar-temporal-object.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaintime
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.zoneddatetime.prototype.withplaintime step 4.a:
+ a. Let _plainTime_ be ? ToTemporalTime(_plainTimeLike_).
+ sec-temporal-totemporaltime step 3.d:
+ d. If _calendar_ is not *undefined*, then
+ i. Set _calendar_ to ? ToTemporalCalendar(_calendar_).
+ ii. If ? ToString(_calendar_) is not *"iso8601"*, then
+ 1. Throw a *RangeError* exception.
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+ assert.throws(RangeError, () => datetime.withPlainTime({ hour: 12, minute: 30, calendar: temporalObject }));
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/length.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/length.js
new file mode 100644
index 00000000000..4510e3bd740
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: Temporal.PlainDateTime.prototype.withPlainTime.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.withPlainTime, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/name.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/name.js
new file mode 100644
index 00000000000..7aaebb269d1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: Temporal.PlainDateTime.prototype.withPlainTime.name is "withPlainTime".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainDateTime.prototype.withPlainTime, "name", {
+ value: "withPlainTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/not-a-constructor.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/not-a-constructor.js
new file mode 100644
index 00000000000..3b443b329d6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: >
+ Temporal.PlainDateTime.prototype.withPlainTime does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainDateTime.prototype.withPlainTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainDateTime.prototype.withPlainTime), false,
+ "isConstructor(Temporal.PlainDateTime.prototype.withPlainTime)");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/prop-desc.js
new file mode 100644
index 00000000000..7a72db65af1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: The "withPlainTime" property of Temporal.PlainDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainDateTime.prototype.withPlainTime,
+ "function",
+ "`typeof PlainDateTime.prototype.withPlainTime` is `function`"
+);
+
+verifyProperty(Temporal.PlainDateTime.prototype, "withPlainTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/subclassing-ignored.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/subclassing-ignored.js
new file mode 100644
index 00000000000..ee56414d1e2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainDateTime,
+ [2000, 5, 2, 12, 34, 56, 987, 654, 321],
+ "withPlainTime",
+ ["05:43:21.123456789"],
+ (result) => TemporalHelpers.assertPlainDateTime(result, 2000, 5, "M05", 2, 5, 43, 21, 123, 456, 789),
+);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/time-undefined.js b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/time-undefined.js
new file mode 100644
index 00000000000..48d6a79a68e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/time-undefined.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: The time is assumed to be midnight if not given
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
+
+const explicit = datetime.withPlainTime(undefined);
+assert.sameValue(explicit.hour, 0, "default time is midnight");
+assert.sameValue(explicit.minute, 0, "default time is midnight");
+assert.sameValue(explicit.second, 0, "default time is midnight");
+assert.sameValue(explicit.millisecond, 0, "default time is midnight");
+assert.sameValue(explicit.microsecond, 0, "default time is midnight");
+assert.sameValue(explicit.nanosecond, 0, "default time is midnight");
+
+const implicit = datetime.withPlainTime();
+assert.sameValue(implicit.hour, 0, "default time is midnight");
+assert.sameValue(implicit.minute, 0, "default time is midnight");
+assert.sameValue(implicit.second, 0, "default time is midnight");
+assert.sameValue(implicit.millisecond, 0, "default time is midnight");
+assert.sameValue(implicit.microsecond, 0, "default time is midnight");
+assert.sameValue(implicit.nanosecond, 0, "default time is midnight");
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/year/calendar-returns-infinity.js b/test/built-ins/Temporal/PlainDateTime/prototype/year/calendar-returns-infinity.js
new file mode 100644
index 00000000000..e963454d24c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/year/calendar-returns-infinity.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.year
+description: Getter throws if the calendar returns ±∞ from its year method
+features: [Temporal]
+---*/
+
+class InfinityCalendar extends Temporal.Calendar {
+ constructor(positive) {
+ super("iso8601");
+ this.positive = positive;
+ }
+
+ year() {
+ return this.positive ? Infinity : -Infinity;
+ }
+}
+
+const pos = new InfinityCalendar(true);
+const instance1 = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321, pos);
+assert.throws(RangeError, () => instance1.year);
+
+const neg = new InfinityCalendar(false);
+const instance2 = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321, neg);
+assert.throws(RangeError, () => instance2.year);
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/year/prop-desc.js b/test/built-ins/Temporal/PlainDateTime/prototype/year/prop-desc.js
new file mode 100644
index 00000000000..173640eeb4d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/year/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.year
+description: The "year" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "year");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainDateTime/second-undefined.js b/test/built-ins/Temporal/PlainDateTime/second-undefined.js
new file mode 100644
index 00000000000..9ec2ea3ef79
--- /dev/null
+++ b/test/built-ins/Temporal/PlainDateTime/second-undefined.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime
+description: Second argument defaults to 0 if not given
+features: [Temporal]
+---*/
+
+const args = [2000, 5, 2, 12, 34];
+
+const explicit = new Temporal.PlainDateTime(...args, undefined);
+assert.sameValue(explicit.second, 0, "second default argument");
+
+const implicit = new Temporal.PlainDateTime(...args);
+assert.sameValue(implicit.second, 0, "second default argument");
diff --git a/test/built-ins/Temporal/PlainMonthDay/basic.js b/test/built-ins/Temporal/PlainMonthDay/basic.js
new file mode 100644
index 00000000000..f854cfb450d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/basic.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday
+description: Basic tests for the PlainMonthDay constructor.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const leapDay = new Temporal.PlainMonthDay(2, 29);
+TemporalHelpers.assertPlainMonthDay(leapDay, "M02", 29, "leap day is supported");
+assert.sameValue(leapDay.calendar.id, "iso8601", "leap day calendar");
+
+const beforeEpoch = new Temporal.PlainMonthDay(12, 2, "iso8601", 1920);
+TemporalHelpers.assertPlainMonthDay(beforeEpoch, "M12", 2, "reference year before epoch", 1920);
+assert.sameValue(beforeEpoch.calendar.id, "iso8601", "reference year before epoch calendar");
+
+const afterEpoch = new Temporal.PlainMonthDay(1, 7, "iso8601", 1980);
+TemporalHelpers.assertPlainMonthDay(afterEpoch, "M01", 7, "reference year after epoch", 1980);
+assert.sameValue(afterEpoch.calendar.id, "iso8601", "reference year after epoch calendar");
diff --git a/test/built-ins/Temporal/PlainMonthDay/builtin.js b/test/built-ins/Temporal/PlainMonthDay/builtin.js
new file mode 100644
index 00000000000..c3058c0bdc5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/builtin.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday
+description: Tests that Temporal.PlainMonthDay meets the requirements for built-in objects
+info: |
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainMonthDay),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainMonthDay),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainMonthDay),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.PlainMonthDay.prototype,
+ "object", "prototype property");
diff --git a/test/built-ins/Temporal/PlainMonthDay/calendar-invalid.js b/test/built-ins/Temporal/PlainMonthDay/calendar-invalid.js
new file mode 100644
index 00000000000..7bc058ef2f1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/calendar-invalid.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainMonthDay throws a RangeError if the calendar argument is invalid
+esid: sec-temporal.plainmonthday
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = ["get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"];
+const actual = [];
+const args = [
+ TemporalHelpers.toPrimitiveObserver(actual, 2, "month"),
+ TemporalHelpers.toPrimitiveObserver(actual, 1, "day"),
+ "local",
+ TemporalHelpers.toPrimitiveObserver(actual, 1, "year")
+];
+assert.throws(RangeError, () => new Temporal.PlainMonthDay(...args));
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainMonthDay/calendar-temporal-object.js b/test/built-ins/Temporal/PlainMonthDay/calendar-temporal-object.js
new file mode 100644
index 00000000000..839d2d07673
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/calendar-temporal-object.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plainmonthday step 5:
+ 5. Let _calendar_ be ? ToTemporalCalendarWithISODefault(_calendarLike_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const result = new Temporal.PlainMonthDay(5, 2, temporalObject);
+ assert.sameValue(result.calendar, calendar, "Temporal object coerced to calendar");
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/calendar-undefined.js b/test/built-ins/Temporal/PlainMonthDay/calendar-undefined.js
new file mode 100644
index 00000000000..2b7d1d6db24
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/calendar-undefined.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday
+description: Calendar argument defaults to the built-in ISO 8601 calendar
+features: [Temporal]
+---*/
+
+const args = [5, 2];
+
+Object.defineProperty(Temporal.Calendar, "from", {
+ get() {
+ throw new Test262Error("Should not get Calendar.from");
+ },
+});
+
+const dateExplicit = new Temporal.PlainMonthDay(...args, undefined);
+assert.sameValue(dateExplicit.calendar.toString(), "iso8601");
+
+const dateImplicit = new Temporal.PlainMonthDay(...args);
+assert.sameValue(dateImplicit.calendar.toString(), "iso8601");
diff --git a/test/built-ins/Temporal/PlainMonthDay/constructor.js b/test/built-ins/Temporal/PlainMonthDay/constructor.js
new file mode 100644
index 00000000000..5df5c2012a0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/constructor.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday
+description: Temporal.PlainMonthDay constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.PlainMonthDay());
diff --git a/test/built-ins/Temporal/PlainMonthDay/from/builtin.js b/test/built-ins/Temporal/PlainMonthDay/from/builtin.js
new file mode 100644
index 00000000000..e5a1e866c9a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/from/builtin.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.from
+description: Tests that Temporal.PlainMonthDay.from meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainMonthDay.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainMonthDay.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainMonthDay.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainMonthDay.from.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainMonthDay/from/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainMonthDay/from/calendar-fields-iterable.js
new file mode 100644
index 00000000000..ad7c230d2a9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/from/calendar-fields-iterable.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.from
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainmonthday.from step 3:
+ 3. Return ? ToTemporalMonthDay(_item_, _options_).
+ sec-temporal-totemporalmonthday step 2.f:
+ f. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+Temporal.PlainMonthDay.from({ monthCode: "M05", day: 2, calendar });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainMonthDay/from/calendar-temporal-object.js b/test/built-ins/Temporal/PlainMonthDay/from/calendar-temporal-object.js
new file mode 100644
index 00000000000..98a61cf2215
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/from/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.from
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plainmonthday.from step 3:
+ 3. Return ? ToTemporalMonthDay(_item_, _options_).
+ sec-temporal-totemporalmonthday step 3.e:
+ e. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const result = Temporal.PlainMonthDay.from({ monthCode: "M05", day: 2, calendar: temporalObject });
+ assert.sameValue(result.calendar, calendar, "Temporal object coerced to calendar");
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/from/fields-leap-day.js b/test/built-ins/Temporal/PlainMonthDay/from/fields-leap-day.js
new file mode 100644
index 00000000000..81aa8555f5b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/from/fields-leap-day.js
@@ -0,0 +1,39 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.from
+description: Basic tests for PlainMonthDay.from(string).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Leap years
+["reject", "constrain"].forEach((overflow) => {
+ const string = Temporal.PlainMonthDay.from("02-29", { overflow });
+ TemporalHelpers.assertPlainMonthDay(string, "M02", 29, `from ${overflow} string`);
+
+ const monthCode = Temporal.PlainMonthDay.from({ monthCode: "M02", day: 29 }, { overflow });
+ TemporalHelpers.assertPlainMonthDay(monthCode, "M02", 29, `from ${overflow} with monthCode`);
+
+ const implicit = Temporal.PlainMonthDay.from({ month: 2, day: 29 }, { overflow });
+ TemporalHelpers.assertPlainMonthDay(implicit, "M02", 29, `from ${overflow} without year`);
+
+ const explicit = Temporal.PlainMonthDay.from({ month: 2, day: 29, year: 1996 }, { overflow });
+ TemporalHelpers.assertPlainMonthDay(explicit, "M02", 29, `from ${overflow} with leap year`);
+});
+
+// Non-leap years
+assert.throws(RangeError,
+ () => Temporal.PlainMonthDay.from({ month: 2, day: 29, year: 2001 }, { overflow: "reject" }),
+ "from reject with non-leap year");
+
+const nonLeap = Temporal.PlainMonthDay.from({ month: 2, day: 29, year: 2001 }, { overflow: "constrain" });
+TemporalHelpers.assertPlainMonthDay(nonLeap, "M02", 28, "from constrain with non-leap year");
+
+assert.throws(RangeError,
+ () => Temporal.PlainMonthDay.from({ month: 2, day: 29, year: 2001, calendar: "iso8601" }, { overflow: "reject" }),
+ "from reject with non-leap year and explicit calendar");
+
+const nonLeapCalendar = Temporal.PlainMonthDay.from({ month: 2, day: 29, year: 2001, calendar: "iso8601" }, { overflow: "constrain" });
+TemporalHelpers.assertPlainMonthDay(nonLeapCalendar, "M02", 28, "from constrain with non-leap year and explicit calendar");
diff --git a/test/built-ins/Temporal/PlainMonthDay/from/fields-missing-properties.js b/test/built-ins/Temporal/PlainMonthDay/from/fields-missing-properties.js
new file mode 100644
index 00000000000..62218219a25
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/from/fields-missing-properties.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.from
+description: Basic tests for PlainMonthDay.from(object) with missing properties.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.PlainMonthDay.from({}), "No properties");
+assert.throws(TypeError, () => Temporal.PlainMonthDay.from({ day: 15 }), "Only day");
+assert.throws(TypeError, () => Temporal.PlainMonthDay.from({ monthCode: 'M12' }), "Only monthCode");
+assert.throws(TypeError, () => Temporal.PlainMonthDay.from({ monthCode: undefined, day: 15 }), "monthCode undefined");
+assert.throws(TypeError, () => Temporal.PlainMonthDay.from({ months: 12, day: 31 }), "months plural");
+assert.throws(TypeError, () => Temporal.PlainMonthDay.from({ month: 11, day: 18, calendar: "iso8601" }), "month, day with calendar");
diff --git a/test/built-ins/Temporal/PlainMonthDay/from/fields-object.js b/test/built-ins/Temporal/PlainMonthDay/from/fields-object.js
new file mode 100644
index 00000000000..b8a90384923
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/from/fields-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.from
+description: Basic tests for PlainMonthDay.from(object).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const tests = [
+ [{ monthCode: "M10", day: 1 }, "option bag with monthCode"],
+ [{ monthCode: "M10", day: 1, year: 2015 }, "option bag with year, monthCode"],
+ [{ month: 10, day: 1 }, "option bag with year, month"],
+ [{ month: 10, day: 1, year: 2015 }, "option bag with year, month"],
+ [{ month: 10, day: 1, days: 31 }, "option bag with plural 'days'"],
+ [new Temporal.PlainMonthDay(10, 1), "PlainMonthDay object"],
+ [Temporal.PlainDate.from("2019-10-01"), "PlainDate object"],
+];
+
+for (const [argument, description = argument] of tests) {
+ const plainMonthDay = Temporal.PlainMonthDay.from(argument);
+ assert.notSameValue(plainMonthDay, argument, `from ${description} converts`);
+ TemporalHelpers.assertPlainMonthDay(plainMonthDay, "M10", 1, `from ${description}`);
+ assert.sameValue(plainMonthDay.calendar.id, "iso8601", `from ${description} calendar`);
+}
diff --git a/test/built-ins/Temporal/PlainMonthDay/from/fields-plainmonthday.js b/test/built-ins/Temporal/PlainMonthDay/from/fields-plainmonthday.js
new file mode 100644
index 00000000000..41db83f7352
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/from/fields-plainmonthday.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.from
+description: Basic tests for PlainMonthDay.from(PlainMonthDay).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get overflow",
+ "get overflow.toString",
+ "call overflow.toString",
+];
+const actual = [];
+const options = {
+ get overflow() {
+ actual.push("get overflow");
+ return TemporalHelpers.toPrimitiveObserver(actual, "reject", "overflow");
+ }
+};
+
+const fields = new Temporal.PlainMonthDay(11, 16, undefined, 1960);
+const result = Temporal.PlainMonthDay.from(fields, options);
+TemporalHelpers.assertPlainMonthDay(result, "M11", 16, "should copy reference year", 1960);
+assert.compareArray(actual, expected, "Should get overflow");
diff --git a/test/built-ins/Temporal/PlainMonthDay/from/fields-string.js b/test/built-ins/Temporal/PlainMonthDay/from/fields-string.js
new file mode 100644
index 00000000000..618e654049b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/from/fields-string.js
@@ -0,0 +1,47 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.from
+description: Basic tests for PlainMonthDay.from(string).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const tests = [
+ "10-01",
+ "1001",
+ "1965-10-01",
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T00:00:00Z",
+ "1976-10-01T152330.1+00:00",
+ "19761001T15:23:30.1+00:00",
+ "1976-10-01T15:23:30.1+0000",
+ "1976-10-01T152330.1+0000",
+ "19761001T15:23:30.1+0000",
+ "19761001T152330.1+00:00",
+ "19761001T152330.1+0000",
+ "+001976-10-01T152330.1+00:00",
+ "+0019761001T15:23:30.1+00:00",
+ "+001976-10-01T15:23:30.1+0000",
+ "+001976-10-01T152330.1+0000",
+ "+0019761001T15:23:30.1+0000",
+ "+0019761001T152330.1+00:00",
+ "+0019761001T152330.1+0000",
+ "1976-10-01T15:23",
+ "1976-10-01T15",
+ "1976-10-01",
+ "--10-01",
+ "--1001",
+ 1001,
+];
+
+for (const argument of tests) {
+ const plainMonthDay = Temporal.PlainMonthDay.from(argument);
+ assert.notSameValue(plainMonthDay, argument, `from ${argument} converts`);
+ TemporalHelpers.assertPlainMonthDay(plainMonthDay, "M10", 1, `from ${argument}`);
+ assert.sameValue(plainMonthDay.calendar.id, "iso8601", `from ${argument} calendar`);
+}
+
+assert.throws(RangeError, () => Temporal.PlainMonthDay.from("11-18junk"), "no junk at end of string");
+
diff --git a/test/built-ins/Temporal/PlainMonthDay/from/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainMonthDay/from/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..676d2bb6b0e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/from/infinity-throws-rangeerror.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainmonthday.from
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ ...base, [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ ...base, [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/from/length.js b/test/built-ins/Temporal/PlainMonthDay/from/length.js
new file mode 100644
index 00000000000..cfc1f4877ce
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/from/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.from
+description: Temporal.PlainMonthDay.from.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainMonthDay.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/from/name.js b/test/built-ins/Temporal/PlainMonthDay/from/name.js
new file mode 100644
index 00000000000..18912cae296
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/from/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.from
+description: Temporal.PlainMonthDay.from.name is "from"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainMonthDay.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/from/not-a-constructor.js b/test/built-ins/Temporal/PlainMonthDay/from/not-a-constructor.js
new file mode 100644
index 00000000000..15b54cd479c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/from/not-a-constructor.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.from
+description: Temporal.PlainMonthDay.from does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainMonthDay.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainMonthDay.from), false,
+ "isConstructor(Temporal.PlainMonthDay.from)");
diff --git a/test/built-ins/Temporal/PlainMonthDay/from/options-invalid.js b/test/built-ins/Temporal/PlainMonthDay/from/options-invalid.js
new file mode 100644
index 00000000000..bc0acf0e48d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/from/options-invalid.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.from
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const fields = { month: 2, day: 31 };
+
+const values = [null, true, "hello", Symbol("foo"), 1, 1n];
+for (const badOptions of values) {
+ assert.throws(TypeError, () => Temporal.PlainMonthDay.from(fields, badOptions));
+}
diff --git a/test/built-ins/Temporal/PlainMonthDay/from/options-undefined.js b/test/built-ins/Temporal/PlainMonthDay/from/options-undefined.js
new file mode 100644
index 00000000000..61a73e506da
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/from/options-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.from
+includes: [temporalHelpers.js]
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const fields = { month: 2, day: 31 };
+
+const explicit = Temporal.PlainMonthDay.from(fields, undefined);
+TemporalHelpers.assertPlainMonthDay(explicit, "M02", 29, "default overflow is constrain");
+
+const implicit = Temporal.PlainMonthDay.from(fields);
+TemporalHelpers.assertPlainMonthDay(implicit, "M02", 29, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainMonthDay/from/order-of-operations.js b/test/built-ins/Temporal/PlainMonthDay/from/order-of-operations.js
new file mode 100644
index 00000000000..3cd92d9d3ea
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/from/order-of-operations.js
@@ -0,0 +1,48 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.from
+description: Properties on an object passed to from() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get calendar",
+ "get day",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get month",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+];
+const actual = [];
+const fields = {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+ day: 1.7,
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ if (key === "calendar") return Temporal.Calendar.from("iso8601");
+ const result = target[key];
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = Temporal.PlainMonthDay.from(argument);
+TemporalHelpers.assertPlainMonthDay(result, "M01", 1);
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainMonthDay/from/overflow-invalid-string.js b/test/built-ins/Temporal/PlainMonthDay/from/overflow-invalid-string.js
new file mode 100644
index 00000000000..c2427006fdc
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/from/overflow-invalid-string.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.from
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporalmonthday steps 3–4:
+ 3. If Type(_item_) is Object, then
+ ...
+ j. Return ? MonthDayFromFields(_calendar_, _fields_, _options_).
+ 4. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plainmonthday.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalMonthDay]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalMonthDay(_item_, _options_).
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainMonthDay(5, 2),
+ { monthCode: "M05", day: 2 },
+ "05-02",
+];
+validValues.forEach((value) => {
+ assert.throws(RangeError, () => Temporal.PlainMonthDay.from(value, { overflow: "other string" }));
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/from/overflow-undefined.js b/test/built-ins/Temporal/PlainMonthDay/from/overflow-undefined.js
new file mode 100644
index 00000000000..1e2161e4473
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/from/overflow-undefined.js
@@ -0,0 +1,45 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.from
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporalmonthday steps 3–4:
+ 3. If Type(_item_) is Object, then
+ ...
+ j. Return ? MonthDayFromFields(_calendar_, _fields_, _options_).
+ 4. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plainmonthday.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalMonthDay]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalMonthDay(_item_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainMonthDay(5, 2),
+ "05-02",
+];
+validValues.forEach((value) => {
+ const explicit = Temporal.PlainMonthDay.from(value, { overflow: undefined });
+ TemporalHelpers.assertPlainMonthDay(explicit, "M05", 2, "overflow is ignored");
+ const implicit = Temporal.PlainMonthDay.from(value, {});
+ TemporalHelpers.assertPlainMonthDay(implicit, "M05", 2, "overflow is ignored");
+ const lambda = Temporal.PlainMonthDay.from(value, () => {});
+ TemporalHelpers.assertPlainMonthDay(lambda, "M05", 2, "overflow is ignored");
+});
+
+const propertyBag = { year: 2000, month: 13, day: 34 };
+const explicit = Temporal.PlainMonthDay.from(propertyBag, { overflow: undefined });
+TemporalHelpers.assertPlainMonthDay(explicit, "M12", 31, "default overflow is constrain");
+const implicit = Temporal.PlainMonthDay.from(propertyBag, {});
+TemporalHelpers.assertPlainMonthDay(implicit, "M12", 31, "default overflow is constrain");
+const lambda = Temporal.PlainMonthDay.from(propertyBag, () => {});
+TemporalHelpers.assertPlainMonthDay(lambda, "M12", 31, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainMonthDay/from/overflow-wrong-type.js b/test/built-ins/Temporal/PlainMonthDay/from/overflow-wrong-type.js
new file mode 100644
index 00000000000..8832fd05a72
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/from/overflow-wrong-type.js
@@ -0,0 +1,34 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.from
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporalmonthday steps 3–4:
+ 3. If Type(_item_) is Object, then
+ ...
+ j. Return ? MonthDayFromFields(_calendar_, _fields_, _options_).
+ 4. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plainmonthday.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalMonthDay]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalMonthDay(_item_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainMonthDay(5, 2),
+ { monthCode: "M05", day: 2 },
+ "05-02",
+];
+validValues.forEach((value) => TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => Temporal.PlainMonthDay.from(value, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainMonthDay(result, "M05", 2, descr),
+));
diff --git a/test/built-ins/Temporal/PlainMonthDay/from/overflow.js b/test/built-ins/Temporal/PlainMonthDay/from/overflow.js
new file mode 100644
index 00000000000..0db03be79a9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/from/overflow.js
@@ -0,0 +1,38 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.from
+description: Handling for overflow option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainMonthDay(5, 2),
+ "05-02",
+];
+validValues.forEach((value) => {
+ const constrain = Temporal.PlainMonthDay.from(value, { overflow: "constrain" });
+ TemporalHelpers.assertPlainMonthDay(constrain, "M05", 2, "overflow is ignored: constrain");
+
+ const reject = Temporal.PlainMonthDay.from(value, { overflow: "reject" });
+ TemporalHelpers.assertPlainMonthDay(reject, "M05", 2, "overflow is ignored: reject");
+});
+
+const propertyBag1 = { year: 2000, month: 13, day: 34 };
+const result1 = Temporal.PlainMonthDay.from(propertyBag1, { overflow: "constrain" });
+TemporalHelpers.assertPlainMonthDay(result1, "M12", 31, "default overflow is constrain");
+assert.throws(RangeError, () => Temporal.PlainMonthDay.from(propertyBag1, { overflow: "reject" }),
+ "invalid property bag: reject");
+
+const propertyBag2 = { month: 1, day: 32 };
+const result2 = Temporal.PlainMonthDay.from(propertyBag2, { overflow: "constrain" });
+TemporalHelpers.assertPlainMonthDay(result2, "M01", 31, "default overflow is constrain");
+assert.throws(RangeError, () => Temporal.PlainMonthDay.from(propertyBag2, { overflow: "reject" }),
+ "invalid property bag: reject");
+
+assert.throws(RangeError, () => Temporal.PlainMonthDay.from("13-34", { overflow: "constrain" }),
+ "invalid ISO string: constrain");
+assert.throws(RangeError, () => Temporal.PlainMonthDay.from("13-34", { overflow: "reject" }),
+ "invalid ISO string: reject");
diff --git a/test/built-ins/Temporal/PlainMonthDay/from/prop-desc.js b/test/built-ins/Temporal/PlainMonthDay/from/prop-desc.js
new file mode 100644
index 00000000000..72eb7528f6e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/from/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.from
+description: The "from" property of Temporal.PlainMonthDay
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainMonthDay.from,
+ "function",
+ "`typeof PlainMonthDay.from` is `function`"
+);
+
+verifyProperty(Temporal.PlainMonthDay, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/from/subclassing-ignored.js b/test/built-ins/Temporal/PlainMonthDay/from/subclassing-ignored.js
new file mode 100644
index 00000000000..083c42e9b17
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/from/subclassing-ignored.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.PlainMonthDay,
+ "from",
+ ["05-02"],
+ (result) => TemporalHelpers.assertPlainMonthDay(result, "M05", 2),
+);
diff --git a/test/built-ins/Temporal/PlainMonthDay/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainMonthDay/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..a96544f0bd0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/infinity-throws-rangeerror.js
@@ -0,0 +1,41 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainMonthDay throws a RangeError if any numerical value is Infinity
+esid: sec-temporal.plainmonthday
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const isoCalendar = Temporal.Calendar.from('iso8601');
+
+assert.throws(RangeError, () => new Temporal.PlainMonthDay(Infinity, 1));
+assert.throws(RangeError, () => new Temporal.PlainMonthDay(1, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainMonthDay(1, 1, isoCalendar, Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite month",
+ [O(Infinity, "month"), O(1, "day"), () => "iso8601", O(1, "year")],
+ ["get month.valueOf", "call month.valueOf"]
+ ],
+ [
+ "infinite day",
+ [O(2, "month"), O(Infinity, "day"), () => "iso8601", O(1, "year")],
+ ["get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"]
+ ],
+ [
+ "infinite year",
+ [O(2, "month"), O(1, "day"), () => "iso8601", O(Infinity, "year")],
+ ["get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf", "get year.valueOf", "call year.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainMonthDay(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
diff --git a/test/built-ins/Temporal/PlainMonthDay/length.js b/test/built-ins/Temporal/PlainMonthDay/length.js
new file mode 100644
index 00000000000..c8927c6c909
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday
+description: Temporal.PlainMonthDay.length is 2
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainMonthDay, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/missing-arguments.js b/test/built-ins/Temporal/PlainMonthDay/missing-arguments.js
new file mode 100644
index 00000000000..62d378a06ee
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/missing-arguments.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday
+description: RangeError thrown after processing given args when invoked without all required args
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get month.valueOf",
+ "call month.valueOf",
+];
+const actual = [];
+const args = [
+ TemporalHelpers.toPrimitiveObserver(actual, 1, "month"),
+];
+
+assert.throws(RangeError, () => new Temporal.PlainMonthDay(...args));
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainMonthDay/name.js b/test/built-ins/Temporal/PlainMonthDay/name.js
new file mode 100644
index 00000000000..da58f9a636f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday
+description: Temporal.PlainMonthDay.name is "PlainMonthDay"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainMonthDay, "name", {
+ value: "PlainMonthDay",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/negative-infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainMonthDay/negative-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..1944f9c6668
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,41 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainMonthDay throws a RangeError if any numerical value is -Infinity
+esid: sec-temporal.plainmonthday
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const isoCalendar = Temporal.Calendar.from('iso8601');
+
+assert.throws(RangeError, () => new Temporal.PlainMonthDay(-Infinity, 1));
+assert.throws(RangeError, () => new Temporal.PlainMonthDay(1, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainMonthDay(1, 1, isoCalendar, -Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite month",
+ [O(-Infinity, "month"), O(1, "day"), () => "iso8601", O(1, "year")],
+ ["get month.valueOf", "call month.valueOf"]
+ ],
+ [
+ "infinite day",
+ [O(2, "month"), O(-Infinity, "day"), () => "iso8601", O(1, "year")],
+ ["get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"]
+ ],
+ [
+ "infinite year",
+ [O(2, "month"), O(1, "day"), () => "iso8601", O(-Infinity, "year")],
+ ["get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf", "get year.valueOf", "call year.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainMonthDay(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
diff --git a/test/built-ins/Temporal/PlainMonthDay/prop-desc.js b/test/built-ins/Temporal/PlainMonthDay/prop-desc.js
new file mode 100644
index 00000000000..830b08e8552
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday
+description: The "PlainMonthDay" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainMonthDay,
+ "function",
+ "`typeof PlainMonthDay` is `function`"
+);
+
+verifyProperty(Temporal, "PlainMonthDay", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/calendar/prop-desc.js b/test/built-ins/Temporal/PlainMonthDay/prototype/calendar/prop-desc.js
new file mode 100644
index 00000000000..a428d164eaa
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/calendar/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainmonthday.prototype.calendar
+description: The "calendar" property of Temporal.PlainMonthDay.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainMonthDay.prototype, "calendar");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/day/basic.js b/test/built-ins/Temporal/PlainMonthDay/prototype/day/basic.js
new file mode 100644
index 00000000000..97566f6d7dc
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/day/basic.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainmonthday.prototype.day
+description: Basic tests for day().
+features: [Temporal]
+---*/
+
+const md = new Temporal.PlainMonthDay(1, 15);
+assert.sameValue(md.day, 15);
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/day/calendar-returns-infinity.js b/test/built-ins/Temporal/PlainMonthDay/prototype/day/calendar-returns-infinity.js
new file mode 100644
index 00000000000..f07e1f3a087
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/day/calendar-returns-infinity.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainmonthday.prototype.day
+description: Getter throws if the calendar returns ±∞ from its day method
+features: [Temporal]
+---*/
+
+class InfinityCalendar extends Temporal.Calendar {
+ constructor(positive) {
+ super("iso8601");
+ this.positive = positive;
+ }
+
+ day() {
+ return this.positive ? Infinity : -Infinity;
+ }
+}
+
+const pos = new InfinityCalendar(true);
+const instance1 = new Temporal.PlainMonthDay(5, 2, pos);
+assert.throws(RangeError, () => instance1.day);
+
+const neg = new InfinityCalendar(false);
+const instance2 = new Temporal.PlainMonthDay(5, 2, neg);
+assert.throws(RangeError, () => instance2.day);
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/day/prop-desc.js b/test/built-ins/Temporal/PlainMonthDay/prototype/day/prop-desc.js
new file mode 100644
index 00000000000..dccb417eda1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/day/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainmonthday.prototype.day
+description: The "day" property of Temporal.PlainMonthDay.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainMonthDay.prototype, "day");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-wrong-type.js b/test/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-wrong-type.js
new file mode 100644
index 00000000000..c89ad73130e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-wrong-type.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.equals
+description: Appropriate error thrown when argument cannot be converted to a valid string
+features: [Symbol, Temporal]
+---*/
+
+const instance = Temporal.PlainMonthDay.from({ month: 5, day: 2 });
+
+assert.throws(RangeError, () => instance.equals(undefined), "undefined");
+assert.throws(RangeError, () => instance.equals(null), "null");
+assert.throws(RangeError, () => instance.equals(true), "true");
+assert.throws(RangeError, () => instance.equals(""), "empty string");
+assert.throws(TypeError, () => instance.equals(Symbol()), "symbol");
+assert.throws(RangeError, () => instance.equals(1), "1");
+assert.throws(TypeError, () => instance.equals({}), "plain object");
+assert.throws(TypeError, () => instance.equals(Temporal.PlainMonthDay), "Temporal.PlainMonthDay");
+assert.throws(TypeError, () => instance.equals(Temporal.PlainMonthDay.prototype), "Temporal.PlainMonthDay.prototype");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/equals/basic.js b/test/built-ins/Temporal/PlainMonthDay/prototype/equals/basic.js
new file mode 100644
index 00000000000..c4bf132844f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/equals/basic.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.equals
+description: Basic tests for equals()
+features: [Temporal]
+---*/
+
+const md1 = Temporal.PlainMonthDay.from("01-22");
+const md2 = Temporal.PlainMonthDay.from("12-15");
+assert(md1.equals(md1), "same object");
+assert.sameValue(md1.equals(md2), false, "different object");
+
+assert(md1.equals("01-22"), "same string");
+assert.sameValue(md2.equals("01-22"), false, "different string");
+
+assert(md1.equals({ month: 1, day: 22 }), "same property bag");
+assert.sameValue(md2.equals({ month: 1, day: 22 }), false, "different property bag");
+
+assert.throws(TypeError, () => md1.equals({ month: 1 }), "missing field in property bag");
+
+const mdYear1 = new Temporal.PlainMonthDay(1, 1, undefined, 1972);
+const mdYear2 = new Temporal.PlainMonthDay(1, 1, undefined, 2000);
+assert.sameValue(mdYear1.equals(mdYear2), false, "different reference years");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/equals/builtin.js b/test/built-ins/Temporal/PlainMonthDay/prototype/equals/builtin.js
new file mode 100644
index 00000000000..c8280a63a95
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/equals/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.equals
+description: >
+ Tests that Temporal.PlainMonthDay.prototype.equals
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainMonthDay.prototype.equals),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainMonthDay.prototype.equals),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainMonthDay.prototype.equals),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainMonthDay.prototype.equals.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-fields-iterable.js
new file mode 100644
index 00000000000..f6780885cd8
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-fields-iterable.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.equals
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainmonthday.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalMonthDay(_other_).
+ sec-temporal-totemporalmonthday step 2.f:
+ f. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const date = new Temporal.PlainMonthDay(5, 2, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+date.equals({ monthCode: "M06", day: 2, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-temporal-object.js b/test/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-temporal-object.js
new file mode 100644
index 00000000000..0bf3b816bf7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.equals
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plainmonthday.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalMonthDay(_other_).
+ sec-temporal-totemporalmonthday step 3.e:
+ e. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const monthday = new Temporal.PlainMonthDay(5, 2, temporalObject);
+ monthday.equals({ monthCode: "M06", day: 2, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/equals/calendars.js b/test/built-ins/Temporal/PlainMonthDay/prototype/equals/calendars.js
new file mode 100644
index 00000000000..63b75cf1b17
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/equals/calendars.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.equals
+description: Basic tests for equals() calendar handling
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get calendar a.toString",
+ "call calendar a.toString",
+ "get calendar b.toString",
+ "call calendar b.toString",
+];
+const actual = [];
+const calendar = (id) => {
+ return TemporalHelpers.toPrimitiveObserver(actual, id, `calendar ${id}`);
+};
+
+const mdA = new Temporal.PlainMonthDay(2, 7, calendar("a"));
+const mdB = new Temporal.PlainMonthDay(2, 7, calendar("b"));
+const mdC = new Temporal.PlainMonthDay(2, 7, calendar("c"), 1974);
+
+assert.sameValue(mdA.equals(mdC), false, "different year");
+assert.compareArray(actual, [], "Should not check calendar");
+
+assert.sameValue(mdA.equals(mdB), false, "different calendar");
+assert.compareArray(actual, expected, "Should check calendar");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/equals/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainMonthDay/prototype/equals/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..4e2d2de96f1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/equals/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainmonthday.prototype.equals
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(5, 2);
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/equals/length.js b/test/built-ins/Temporal/PlainMonthDay/prototype/equals/length.js
new file mode 100644
index 00000000000..5f4b88fc4f7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/equals/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.equals
+description: Temporal.PlainMonthDay.prototype.equals.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainMonthDay.prototype.equals, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/equals/name.js b/test/built-ins/Temporal/PlainMonthDay/prototype/equals/name.js
new file mode 100644
index 00000000000..23f16a314a5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/equals/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.equals
+description: Temporal.PlainMonthDay.prototype.equals.name is "equals".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainMonthDay.prototype.equals, "name", {
+ value: "equals",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/equals/not-a-constructor.js b/test/built-ins/Temporal/PlainMonthDay/prototype/equals/not-a-constructor.js
new file mode 100644
index 00000000000..7cf181c63b6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/equals/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.equals
+description: >
+ Temporal.PlainMonthDay.prototype.equals does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainMonthDay.prototype.equals();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainMonthDay.prototype.equals), false,
+ "isConstructor(Temporal.PlainMonthDay.prototype.equals)");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/equals/prop-desc.js b/test/built-ins/Temporal/PlainMonthDay/prototype/equals/prop-desc.js
new file mode 100644
index 00000000000..01b5adaeb40
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/equals/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.equals
+description: The "equals" property of Temporal.PlainMonthDay.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainMonthDay.prototype.equals,
+ "function",
+ "`typeof PlainMonthDay.prototype.equals` is `function`"
+);
+
+verifyProperty(Temporal.PlainMonthDay.prototype, "equals", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/builtin.js b/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/builtin.js
new file mode 100644
index 00000000000..c42bb6002c2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.getisofields
+description: >
+ Tests that Temporal.PlainMonthDay.prototype.getISOFields
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainMonthDay.prototype.getISOFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainMonthDay.prototype.getISOFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainMonthDay.prototype.getISOFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainMonthDay.prototype.getISOFields.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/field-names.js b/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/field-names.js
new file mode 100644
index 00000000000..87cedb2153a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/field-names.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.getisofields
+description: Correct field names on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const md = new Temporal.PlainMonthDay(5, 2);
+
+const result = md.getISOFields();
+assert.sameValue(result.isoYear, 1972, "isoYear result");
+assert.sameValue(result.isoMonth, 5, "isoMonth result");
+assert.sameValue(result.isoDay, 2, "isoDay result");
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/field-prop-desc.js b/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/field-prop-desc.js
new file mode 100644
index 00000000000..e74b10df94b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/field-prop-desc.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.getisofields
+description: Properties on the returned object have the correct descriptor
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoMonth",
+ "isoYear",
+];
+
+const md = new Temporal.PlainMonthDay(5, 2);
+const result = md.getISOFields();
+
+for (const property of expected) {
+ verifyProperty(result, property, {
+ writable: true,
+ enumerable: true,
+ configurable: true,
+ });
+}
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/field-traversal-order.js b/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/field-traversal-order.js
new file mode 100644
index 00000000000..9d668403fe8
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/field-traversal-order.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.getisofields
+description: Properties added in correct order to object returned from getISOFields
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoMonth",
+ "isoYear",
+];
+
+const md = new Temporal.PlainMonthDay(5, 2);
+const result = md.getISOFields();
+
+assert.compareArray(Object.keys(result), expected);
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/length.js b/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/length.js
new file mode 100644
index 00000000000..2777d1aa0db
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.getisofields
+description: Temporal.PlainMonthDay.prototype.getISOFields.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainMonthDay.prototype.getISOFields, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/name.js b/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/name.js
new file mode 100644
index 00000000000..62b63c4be04
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.getisofields
+description: Temporal.PlainMonthDay.prototype.getISOFields.name is "getISOFields".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainMonthDay.prototype.getISOFields, "name", {
+ value: "getISOFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/not-a-constructor.js b/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/not-a-constructor.js
new file mode 100644
index 00000000000..4d422b1df38
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.getisofields
+description: >
+ Temporal.PlainMonthDay.prototype.getISOFields does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainMonthDay.prototype.getISOFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainMonthDay.prototype.getISOFields), false,
+ "isConstructor(Temporal.PlainMonthDay.prototype.getISOFields)");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/prop-desc.js b/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/prop-desc.js
new file mode 100644
index 00000000000..3edb7a1429c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/getISOFields/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.getisofields
+description: The "getISOFields" property of Temporal.PlainMonthDay.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainMonthDay.prototype.getISOFields,
+ "function",
+ "`typeof PlainMonthDay.prototype.getISOFields` is `function`"
+);
+
+verifyProperty(Temporal.PlainMonthDay.prototype, "getISOFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/month/unsupported.js b/test/built-ins/Temporal/PlainMonthDay/prototype/month/unsupported.js
new file mode 100644
index 00000000000..dd8b13c0c11
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/month/unsupported.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainmonthday.prototype.monthcode
+description: There is no 'month' property on Temporal.PlainMonthDay
+features: [Temporal]
+---*/
+
+const md = new Temporal.PlainMonthDay(1, 15);
+assert.sameValue(md.month, undefined);
+assert.sameValue("month" in md, false);
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/monthCode/basic.js b/test/built-ins/Temporal/PlainMonthDay/prototype/monthCode/basic.js
new file mode 100644
index 00000000000..1ab3c75a373
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/monthCode/basic.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainmonthday.prototype.monthcode
+description: Basic tests for monthCode().
+features: [Temporal]
+---*/
+
+const md = new Temporal.PlainMonthDay(1, 15);
+assert.sameValue(md.monthCode, "M01");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/monthCode/prop-desc.js b/test/built-ins/Temporal/PlainMonthDay/prototype/monthCode/prop-desc.js
new file mode 100644
index 00000000000..d0a88a55197
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/monthCode/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainmonthday.prototype.monthcode
+description: The "monthCode" property of Temporal.PlainMonthDay.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainMonthDay.prototype, "monthCode");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toJSON/builtin.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toJSON/builtin.js
new file mode 100644
index 00000000000..b451fb15bfe
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toJSON/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.tojson
+description: >
+ Tests that Temporal.PlainMonthDay.prototype.toJSON
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainMonthDay.prototype.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainMonthDay.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainMonthDay.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainMonthDay.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toJSON/calendarname.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toJSON/calendarname.js
new file mode 100644
index 00000000000..a590710d694
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toJSON/calendarname.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.protoype.tojson
+description: toJSON doesn't take calendarName into account.
+features: [Temporal]
+---*/
+
+const tests = [
+ [[], "05-02"],
+ [[{ toString() { return "custom"; } }], "1972-05-02[u-ca=custom]"],
+ [[{ toString() { return "iso8601"; } }], "05-02"],
+ [[{ toString() { return "ISO8601"; } }], "1972-05-02[u-ca=ISO8601]"],
+ [[{ toString() { return "\u0131so8601"; } }], "1972-05-02[u-ca=\u0131so8601]"], // dotless i
+];
+const options = {
+ get calendarName() {
+ TemporalHelpers.assertUnreachable("calendarName should not be accessed");
+ return "";
+ }
+};
+
+for (const [args, expected] of tests) {
+ const monthday = new Temporal.PlainMonthDay(5, 2, ...args);
+ const result = monthday.toJSON(options);
+ assert.sameValue(result, expected);
+}
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toJSON/length.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toJSON/length.js
new file mode 100644
index 00000000000..7a7116091e9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toJSON/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.tojson
+description: Temporal.PlainMonthDay.prototype.toJSON.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainMonthDay.prototype.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toJSON/name.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toJSON/name.js
new file mode 100644
index 00000000000..021aa0724a4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toJSON/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.tojson
+description: Temporal.PlainMonthDay.prototype.toJSON.name is "toJSON".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainMonthDay.prototype.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toJSON/not-a-constructor.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 00000000000..9ff94e212ae
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toJSON/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.tojson
+description: >
+ Temporal.PlainMonthDay.prototype.toJSON does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainMonthDay.prototype.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainMonthDay.prototype.toJSON), false,
+ "isConstructor(Temporal.PlainMonthDay.prototype.toJSON)");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toJSON/prop-desc.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toJSON/prop-desc.js
new file mode 100644
index 00000000000..c8bffd2b906
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toJSON/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.tojson
+description: The "toJSON" property of Temporal.PlainMonthDay.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainMonthDay.prototype.toJSON,
+ "function",
+ "`typeof PlainMonthDay.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.PlainMonthDay.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/builtin.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/builtin.js
new file mode 100644
index 00000000000..8f4db7271cb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.tolocalestring
+description: >
+ Tests that Temporal.PlainMonthDay.prototype.toLocaleString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainMonthDay.prototype.toLocaleString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainMonthDay.prototype.toLocaleString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainMonthDay.prototype.toLocaleString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainMonthDay.prototype.toLocaleString.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/length.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/length.js
new file mode 100644
index 00000000000..7b647b1e477
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.tolocalestring
+description: Temporal.PlainMonthDay.prototype.toLocaleString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainMonthDay.prototype.toLocaleString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/locales-undefined.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/locales-undefined.js
new file mode 100644
index 00000000000..b3f91e23d4d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/locales-undefined.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.tolocalestring
+description: Omitting the locales argument defaults to the DateTimeFormat default
+features: [Temporal]
+---*/
+
+const defaultFormatter = new Intl.DateTimeFormat([], Object.create(null));
+const { calendar } = defaultFormatter.resolvedOptions();
+const monthday = new Temporal.PlainMonthDay(5, 2, calendar);
+const expected = defaultFormatter.format(monthday);
+
+const actualExplicit = monthday.toLocaleString(undefined);
+assert.sameValue(actualExplicit, expected, "default locale is determined by Intl.DateTimeFormat");
+
+const actualImplicit = monthday.toLocaleString();
+assert.sameValue(actualImplicit, expected, "default locale is determined by Intl.DateTimeFormat");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/name.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/name.js
new file mode 100644
index 00000000000..b24cb9f3707
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.tolocalestring
+description: Temporal.PlainMonthDay.prototype.toLocaleString.name is "toLocaleString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainMonthDay.prototype.toLocaleString, "name", {
+ value: "toLocaleString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/not-a-constructor.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/not-a-constructor.js
new file mode 100644
index 00000000000..51a538d372e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.tolocalestring
+description: >
+ Temporal.PlainMonthDay.prototype.toLocaleString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainMonthDay.prototype.toLocaleString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainMonthDay.prototype.toLocaleString), false,
+ "isConstructor(Temporal.PlainMonthDay.prototype.toLocaleString)");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/options-undefined.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/options-undefined.js
new file mode 100644
index 00000000000..3c021fc7a34
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/options-undefined.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.tolocalestring
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const defaultFormatter = new Intl.DateTimeFormat('en', Object.create(null));
+const { calendar } = defaultFormatter.resolvedOptions();
+const monthday = new Temporal.PlainMonthDay(5, 2, calendar);
+const expected = defaultFormatter.format(monthday);
+
+const actualExplicit = monthday.toLocaleString('en', undefined);
+assert.sameValue(actualExplicit, expected, "default locale is determined by Intl.DateTimeFormat");
+
+const actualImplicit = monthday.toLocaleString('en');
+assert.sameValue(actualImplicit, expected, "default locale is determined by Intl.DateTimeFormat");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/prop-desc.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/prop-desc.js
new file mode 100644
index 00000000000..878ff5f9c25
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toLocaleString/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.tolocalestring
+description: The "toLocaleString" property of Temporal.PlainMonthDay.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainMonthDay.prototype.toLocaleString,
+ "function",
+ "`typeof PlainMonthDay.prototype.toLocaleString` is `function`"
+);
+
+verifyProperty(Temporal.PlainMonthDay.prototype, "toLocaleString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/argument-not-object.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/argument-not-object.js
new file mode 100644
index 00000000000..a64f4d2fbb3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/argument-not-object.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.toplaindate
+description: Throws a TypeError if the argument is not an Object, before any other observable actions
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[null, undefined, true, 3.1416, "a string", Symbol("symbol"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarThrowEverything();
+ const plainMonthDay = new Temporal.PlainMonthDay(5, 2, calendar);
+ assert.throws(TypeError, () => plainMonthDay.toPlainDate(primitive));
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/basic.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/basic.js
new file mode 100644
index 00000000000..efb577adc9e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/basic.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.toplaindate
+description: Basic tests for toPlainDate().
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const md = Temporal.PlainMonthDay.from("01-22");
+const d = md.toPlainDate({ year: 2002 });
+TemporalHelpers.assertPlainDate(d, 2002, 1, "M01", 22);
+
+assert.throws(TypeError, () => md.toPlainDate({ something: 'nothing' }), "missing fields");
+
+const leapDay = Temporal.PlainMonthDay.from('02-29');
+TemporalHelpers.assertPlainDate(leapDay.toPlainDate({ year: 2019 }), 2019, 2, "M02", 28);
+TemporalHelpers.assertPlainDate(leapDay.toPlainDate({ year: 2020 }), 2020, 2, "M02", 29);
+
+const options = {
+ get overflow() {
+ TemporalHelpers.assertUnreachable("Should not get overflow option");
+ return "";
+ }
+};
+TemporalHelpers.assertPlainDate(leapDay.toPlainDate({ year: 2019 }, options), 2019, 2, "M02", 28);
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/builtin.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/builtin.js
new file mode 100644
index 00000000000..5846613fa27
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.toplaindate
+description: >
+ Tests that Temporal.PlainMonthDay.prototype.toPlainDate
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainMonthDay.prototype.toPlainDate),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainMonthDay.prototype.toPlainDate),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainMonthDay.prototype.toPlainDate),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainMonthDay.prototype.toPlainDate.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/calendar-fields-iterable.js
new file mode 100644
index 00000000000..69211482090
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/calendar-fields-iterable.js
@@ -0,0 +1,34 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.toplaindate
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainmonthday.prototype.toplaindate step 4:
+ 4. Let _receiverFieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"monthCode"* »).
+ sec-temporal.plainmonthday.prototype.toplaindate step 7:
+ 7. Let _inputFieldNames_ be ? CalendarFields(_calendar_, « *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "day",
+ "monthCode",
+];
+const expected2 = [
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const monthday = new Temporal.PlainMonthDay(5, 2, calendar);
+monthday.toPlainDate({ year: 1997 });
+
+assert.sameValue(calendar.fieldsCallCount, 2, "fields() method called twice");
+assert.compareArray(calendar.fieldsCalledWith[0], expected1, "fields() method called first time with correct args");
+assert.compareArray(calendar.fieldsCalledWith[1], expected2, "fields() method called second time with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole first iterable");
+assert(calendar.iteratorExhausted[1], "iterated through the whole second iterable");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/calendar-merge-fields-returns-primitive.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/calendar-merge-fields-returns-primitive.js
new file mode 100644
index 00000000000..307cb68a81e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/calendar-merge-fields-returns-primitive.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.toplaindate
+description: >
+ with() should throw a TypeError if mergeFields() returns a primitive,
+ without passing the value on to any other calendar methods
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[undefined, null, true, 3.14159, "bad value", Symbol("no"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarMergeFieldsReturnsPrimitive(primitive);
+ const instance = new Temporal.PlainMonthDay(5, 2, calendar);
+ assert.throws(TypeError, () => instance.toPlainDate({ year: 2005 }), "bad return from mergeFields() throws");
+ assert.sameValue(calendar.dateFromFieldsCallCount, 0, "dateFromFields() never called");
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/copies-merge-fields-object.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/copies-merge-fields-object.js
new file mode 100644
index 00000000000..3c0f4b69b5a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/copies-merge-fields-object.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.toplaindate
+description: The object returned from mergeFields() is copied before being passed to monthDayFromFields().
+info: |
+ sec-temporal.plainmonthday.prototype.toplaindate steps 9 and 11:
+ 9. Let _mergedFields_ be ? CalendarMergeFields(_calendar_, _fields_, _inputFields_).
+ 11. Set _mergedFields_ to ? PrepareTemporalFields(_mergedFields_, _mergedFieldNames_, «»).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get day",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+];
+
+const calendar = TemporalHelpers.calendarMergeFieldsGetters();
+const monthday = new Temporal.PlainMonthDay(3, 31, calendar);
+monthday.toPlainDate({ year: 2000 });
+
+assert.compareArray(calendar.mergeFieldsReturnOperations, expected, "getters called on mergeFields return");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..1c1597f204b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/infinity-throws-rangeerror.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws a RangeError if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainmonthday.prototype.toplaindate
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(5, 2);
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.toPlainDate({ year: inf }), `year property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "year");
+ assert.throws(RangeError, () => instance.toPlainDate({ year: obj }));
+ assert.compareArray(calls, ["get year.valueOf", "call year.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/length.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/length.js
new file mode 100644
index 00000000000..1ba7ab257a4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.toplaindate
+description: Temporal.PlainMonthDay.prototype.toPlainDate.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainMonthDay.prototype.toPlainDate, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/name.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/name.js
new file mode 100644
index 00000000000..d2e65880b77
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.toplaindate
+description: Temporal.PlainMonthDay.prototype.toPlainDate.name is "toPlainDate".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainMonthDay.prototype.toPlainDate, "name", {
+ value: "toPlainDate",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/not-a-constructor.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/not-a-constructor.js
new file mode 100644
index 00000000000..b0b1962675a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.toplaindate
+description: >
+ Temporal.PlainMonthDay.prototype.toPlainDate does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainMonthDay.prototype.toPlainDate();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainMonthDay.prototype.toPlainDate), false,
+ "isConstructor(Temporal.PlainMonthDay.prototype.toPlainDate)");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/prop-desc.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/prop-desc.js
new file mode 100644
index 00000000000..4172671533a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toPlainDate/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.toplaindate
+description: The "toPlainDate" property of Temporal.PlainMonthDay.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainMonthDay.prototype.toPlainDate,
+ "function",
+ "`typeof PlainMonthDay.prototype.toPlainDate` is `function`"
+);
+
+verifyProperty(Temporal.PlainMonthDay.prototype, "toPlainDate", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toString/builtin.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/builtin.js
new file mode 100644
index 00000000000..361417cbf14
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.tostring
+description: >
+ Tests that Temporal.PlainMonthDay.prototype.toString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainMonthDay.prototype.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainMonthDay.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainMonthDay.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainMonthDay.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-always.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-always.js
new file mode 100644
index 00000000000..fadff8929f4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-always.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.protoype.tostring
+description: If calendarName is "always", the calendar ID should be included.
+features: [Temporal]
+---*/
+
+const tests = [
+ [[], "05-02[u-ca=iso8601]"],
+ [[{ toString() { return "custom"; } }], "1972-05-02[u-ca=custom]"],
+ [[{ toString() { return "iso8601"; } }], "05-02[u-ca=iso8601]"],
+ [[{ toString() { return "ISO8601"; } }], "1972-05-02[u-ca=ISO8601]"],
+ [[{ toString() { return "\u0131so8601"; } }], "1972-05-02[u-ca=\u0131so8601]"], // dotless i
+];
+
+for (const [args, expected] of tests) {
+ const monthday = new Temporal.PlainMonthDay(5, 2, ...args);
+ const result = monthday.toString({ calendarName: "always" });
+ assert.sameValue(result, expected);
+}
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-auto.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-auto.js
new file mode 100644
index 00000000000..c5f75d787d6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-auto.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.protoype.tostring
+description: If calendarName is "auto", "iso8601" should be omitted.
+features: [Temporal]
+---*/
+
+const tests = [
+ [[], "05-02"],
+ [[{ toString() { return "custom"; } }], "1972-05-02[u-ca=custom]"],
+ [[{ toString() { return "iso8601"; } }], "05-02"],
+ [[{ toString() { return "ISO8601"; } }], "1972-05-02[u-ca=ISO8601]"],
+ [[{ toString() { return "\u0131so8601"; } }], "1972-05-02[u-ca=\u0131so8601]"], // dotless i
+];
+
+for (const [args, expected] of tests) {
+ const monthday = new Temporal.PlainMonthDay(5, 2, ...args);
+ const result = monthday.toString({ calendarName: "auto" });
+ assert.sameValue(result, expected);
+}
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-invalid-string.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-invalid-string.js
new file mode 100644
index 00000000000..5e702560d86
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-invalid-string.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.protoype.tostring
+description: RangeError thrown when calendarName option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « String », « *"auto"*, *"always"*, *"never"* », *"auto"*).
+ sec-temporal.plainmonthday.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const monthday = new Temporal.PlainMonthDay(5, 2);
+for (const calendarName of ["ALWAYS", "sometimes", "other string"]) {
+ assert.throws(RangeError, () => monthday.toString({ calendarName }));
+}
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-never.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-never.js
new file mode 100644
index 00000000000..cee35d9bde6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-never.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.protoype.tostring
+description: If calendarName is "never", the calendar ID should be omitted.
+features: [Temporal]
+---*/
+
+const tests = [
+ [[], "05-02"],
+ [[{ toString() { return "custom"; } }], "1972-05-02"],
+ [[{ toString() { return "iso8601"; } }], "05-02"],
+ [[{ toString() { return "ISO8601"; } }], "1972-05-02"],
+ [[{ toString() { return "\u0131so8601"; } }], "1972-05-02"], // dotless i
+];
+
+for (const [args, expected] of tests) {
+ const monthday = new Temporal.PlainMonthDay(5, 2, ...args);
+ const result = monthday.toString({ calendarName: "never" });
+ assert.sameValue(result, expected);
+}
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-undefined.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-undefined.js
new file mode 100644
index 00000000000..b59e15d916c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-undefined.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.protoype.tostring
+description: Fallback value for calendarName option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « String », « *"auto"*, *"always"*, *"never"* », *"auto"*).
+ sec-temporal.plainmonthday.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const tests = [
+ [[], "05-02"],
+ [[{ toString() { return "custom"; } }], "1972-05-02[u-ca=custom]"],
+ [[{ toString() { return "iso8601"; } }], "05-02"],
+ [[{ toString() { return "ISO8601"; } }], "1972-05-02[u-ca=ISO8601]"],
+ [[{ toString() { return "\u0131so8601"; } }], "1972-05-02[u-ca=\u0131so8601]"], // dotless i
+];
+
+for (const [args, expected] of tests) {
+ const monthday = new Temporal.PlainMonthDay(5, 2, ...args);
+ const explicit = monthday.toString({ calendarName: undefined });
+ assert.sameValue(explicit, expected, "default calendarName option is auto");
+
+ const implicit = monthday.toString({});
+ assert.sameValue(implicit, expected, "default calendarName option is auto");
+}
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-wrong-type.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-wrong-type.js
new file mode 100644
index 00000000000..260c7d3c877
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-wrong-type.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.protoype.tostring
+description: Type conversions for calendarName option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « String », « *"auto"*, *"always"*, *"never"* », *"auto"*).
+ sec-temporal.plainmonthday.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = {
+ toString() { return "custom"; }
+};
+const monthday = new Temporal.PlainMonthDay(5, 2, calendar);
+
+TemporalHelpers.checkStringOptionWrongType("calendarName", "auto",
+ (calendarName) => monthday.toString({ calendarName }),
+ (result, descr) => assert.sameValue(result, "1972-05-02[u-ca=custom]", descr),
+);
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toString/length.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/length.js
new file mode 100644
index 00000000000..b1990cece96
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.tostring
+description: Temporal.PlainMonthDay.prototype.toString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainMonthDay.prototype.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toString/name.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/name.js
new file mode 100644
index 00000000000..0a14932aefc
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.tostring
+description: Temporal.PlainMonthDay.prototype.toString.name is "toString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainMonthDay.prototype.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toString/not-a-constructor.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/not-a-constructor.js
new file mode 100644
index 00000000000..90bd397139e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.tostring
+description: >
+ Temporal.PlainMonthDay.prototype.toString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainMonthDay.prototype.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainMonthDay.prototype.toString), false,
+ "isConstructor(Temporal.PlainMonthDay.prototype.toString)");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toString/options-undefined.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/options-undefined.js
new file mode 100644
index 00000000000..da53b9a72c3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/options-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.tostring
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const tests = [
+ [[], "05-02"],
+ [[{ toString() { return "custom"; } }], "1972-05-02[u-ca=custom]"],
+ [[{ toString() { return "iso8601"; } }], "05-02"],
+ [[{ toString() { return "ISO8601"; } }], "1972-05-02[u-ca=ISO8601]"],
+ [[{ toString() { return "\u0131so8601"; } }], "1972-05-02[u-ca=\u0131so8601]"], // dotless i
+];
+
+for (const [args, expected] of tests) {
+ const monthday = new Temporal.PlainMonthDay(5, 2, ...args);
+ const explicit = monthday.toString(undefined);
+ assert.sameValue(explicit, expected, "default calendarName option is auto");
+
+ const implicit = monthday.toString();
+ assert.sameValue(implicit, expected, "default calendarName option is auto");
+}
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/toString/prop-desc.js b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/prop-desc.js
new file mode 100644
index 00000000000..482f29ba2c6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/toString/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.tostring
+description: The "toString" property of Temporal.PlainMonthDay.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainMonthDay.prototype.toString,
+ "function",
+ "`typeof PlainMonthDay.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.PlainMonthDay.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/valueOf/basic.js b/test/built-ins/Temporal/PlainMonthDay/prototype/valueOf/basic.js
new file mode 100644
index 00000000000..4c617d95c15
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/valueOf/basic.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.valueof
+description: Basic tests for valueOf().
+features: [Temporal]
+---*/
+
+const plainMonthDay = Temporal.PlainMonthDay.from("1963-02-13");
+const plainMonthDay2 = Temporal.PlainMonthDay.from("1963-02-13");
+
+assert.throws(TypeError, () => plainMonthDay.valueOf(), "valueOf");
+assert.throws(TypeError, () => plainMonthDay < plainMonthDay, "<");
+assert.throws(TypeError, () => plainMonthDay <= plainMonthDay, "<=");
+assert.throws(TypeError, () => plainMonthDay > plainMonthDay, ">");
+assert.throws(TypeError, () => plainMonthDay >= plainMonthDay, ">=");
+assert.sameValue(plainMonthDay === plainMonthDay, true, "===");
+assert.sameValue(plainMonthDay === plainMonthDay2, false, "===");
+assert.sameValue(plainMonthDay !== plainMonthDay, false, "!==");
+assert.sameValue(plainMonthDay !== plainMonthDay2, true, "!==");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/valueOf/builtin.js b/test/built-ins/Temporal/PlainMonthDay/prototype/valueOf/builtin.js
new file mode 100644
index 00000000000..2fcbf7121ad
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/valueOf/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.valueof
+description: >
+ Tests that Temporal.PlainMonthDay.prototype.valueOf
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainMonthDay.prototype.valueOf),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainMonthDay.prototype.valueOf),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainMonthDay.prototype.valueOf),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainMonthDay.prototype.valueOf.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/valueOf/length.js b/test/built-ins/Temporal/PlainMonthDay/prototype/valueOf/length.js
new file mode 100644
index 00000000000..a70a8f67d2b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/valueOf/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.valueof
+description: Temporal.PlainMonthDay.prototype.valueOf.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainMonthDay.prototype.valueOf, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/valueOf/name.js b/test/built-ins/Temporal/PlainMonthDay/prototype/valueOf/name.js
new file mode 100644
index 00000000000..cac08d14fa7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/valueOf/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.valueof
+description: Temporal.PlainMonthDay.prototype.valueOf.name is "valueOf".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainMonthDay.prototype.valueOf, "name", {
+ value: "valueOf",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/valueOf/not-a-constructor.js b/test/built-ins/Temporal/PlainMonthDay/prototype/valueOf/not-a-constructor.js
new file mode 100644
index 00000000000..e04c8ed2594
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/valueOf/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.valueof
+description: >
+ Temporal.PlainMonthDay.prototype.valueOf does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainMonthDay.prototype.valueOf();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainMonthDay.prototype.valueOf), false,
+ "isConstructor(Temporal.PlainMonthDay.prototype.valueOf)");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/valueOf/prop-desc.js b/test/built-ins/Temporal/PlainMonthDay/prototype/valueOf/prop-desc.js
new file mode 100644
index 00000000000..b270e38b24b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/valueOf/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.valueof
+description: The "valueOf" property of Temporal.PlainMonthDay.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainMonthDay.prototype.valueOf,
+ "function",
+ "`typeof PlainMonthDay.prototype.valueOf` is `function`"
+);
+
+verifyProperty(Temporal.PlainMonthDay.prototype, "valueOf", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/with/basic.js b/test/built-ins/Temporal/PlainMonthDay/prototype/with/basic.js
new file mode 100644
index 00000000000..0fb248efecf
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/with/basic.js
@@ -0,0 +1,40 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.with
+description: Basic tests for with().
+includes: [compareArray.js, temporalHelpers.js]
+features: [Symbol, Temporal]
+---*/
+
+const md = Temporal.PlainMonthDay.from("01-15");
+
+TemporalHelpers.assertPlainMonthDay(md.with({ day: 22 }),
+ "M01", 22, "with({day})");
+
+TemporalHelpers.assertPlainMonthDay(md.with({ monthCode: "M12" }),
+ "M12", 15, "with({monthCode})");
+
+assert.throws(TypeError, () => md.with({ month: 12 }), "with({month})");
+
+TemporalHelpers.assertPlainMonthDay(md.with({ month: 12, monthCode: "M12" }),
+ "M12", 15, "with({month, monthCode}) agree");
+
+assert.throws(RangeError, () => md.with({ month: 12, monthCode: "M11" }), "with({month, monthCode}) disagree");
+
+TemporalHelpers.assertPlainMonthDay(md.with({ year: 2000, month: 12 }),
+ "M12", 15, "with({year, month})");
+
+TemporalHelpers.assertPlainMonthDay(md.with({ year: 2000 }),
+ "M01", 15, "with({year})");
+
+assert.throws(TypeError, () => md.with({ day: 1, calendar: "iso8601" }), "with({calendar})");
+
+assert.throws(TypeError, () => md.with({ day: 1, timeZone: "UTC" }), "with({timeZone})");
+
+assert.throws(TypeError, () => md.with({}), "with({})");
+assert.throws(TypeError, () => md.with({ months: 12 }), "with({months})");
+
+TemporalHelpers.assertPlainMonthDay(md.with({ monthCode: "M12", days: 1 }),
+ "M12", 15, "with({monthCode, days})");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/with/builtin.js b/test/built-ins/Temporal/PlainMonthDay/prototype/with/builtin.js
new file mode 100644
index 00000000000..57bd87f06cf
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/with/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.with
+description: >
+ Tests that Temporal.PlainMonthDay.prototype.with
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainMonthDay.prototype.with),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainMonthDay.prototype.with),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainMonthDay.prototype.with),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainMonthDay.prototype.with.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-arguments.js b/test/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-arguments.js
new file mode 100644
index 00000000000..aee39245f96
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-arguments.js
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.with
+description: Correct options value is passed to calendar method
+info: |
+ MonthDayFromFields ( calendar, fields [ , options ] )
+
+ 5. Let monthDay be ? Invoke(calendar, "monthDayFromFields", « fields, options »).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const options = {};
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthDayFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.sameValue(args[1], options, "args[1]");
+ return super.monthDayFromFields(...args);
+ }
+}
+const plainMonthDay = new Temporal.PlainMonthDay(7, 2, new CustomCalendar());
+const result = plainMonthDay.with({ monthCode: "M05" }, options);
+TemporalHelpers.assertPlainMonthDay(result, "M05", 2);
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-fields-iterable.js
new file mode 100644
index 00000000000..cd4a94896fb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-fields-iterable.js
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.with
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainmonthday.prototype.with step 9:
+ 9. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const monthday = new Temporal.PlainMonthDay(5, 2, calendar);
+monthday.with({ day: 6 });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-merge-fields-returns-primitive.js b/test/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-merge-fields-returns-primitive.js
new file mode 100644
index 00000000000..5cf1657accf
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/with/calendar-merge-fields-returns-primitive.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.with
+description: >
+ with() should throw a TypeError if mergeFields() returns a primitive,
+ without passing the value on to any other calendar methods
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[undefined, null, true, 3.14159, "bad value", Symbol("no"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarMergeFieldsReturnsPrimitive(primitive);
+ const instance = new Temporal.PlainMonthDay(5, 2, calendar);
+ assert.throws(TypeError, () => instance.with({ year: 2005 }), "bad return from mergeFields() throws");
+ assert.sameValue(calendar.monthDayFromFieldsCallCount, 0, "monthDayFromFields() never called");
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/with/copies-merge-fields-object.js b/test/built-ins/Temporal/PlainMonthDay/prototype/with/copies-merge-fields-object.js
new file mode 100644
index 00000000000..ee7d3e41228
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/with/copies-merge-fields-object.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.with
+description: The object returned from mergeFields() is copied before being passed to monthDayFromFields().
+info: |
+ sec-temporal.plainmonthday.prototype.with steps 13–15:
+ 13. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialMonthDay_).
+ 14. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, «»).
+ 15. Return ? MonthDayFromFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get day",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get month", // PlainMonthDay.month property does not exist, no valueOf
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get year", // undefined, no valueOf
+];
+
+const calendar = TemporalHelpers.calendarMergeFieldsGetters();
+const monthday = new Temporal.PlainMonthDay(3, 31, calendar);
+monthday.with({ day: 1 });
+
+assert.compareArray(calendar.mergeFieldsReturnOperations, expected, "getters called on mergeFields return");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/with/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainMonthDay/prototype/with/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..339ef7b0cc0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/with/infinity-throws-rangeerror.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainmonthday.prototype.with
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(5, 2);
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.with({ day: inf }, { overflow }), `day property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "day");
+ assert.throws(RangeError, () => instance.with({ day: obj }, { overflow }));
+ assert.compareArray(calls, ["get day.valueOf", "call day.valueOf"], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/with/length.js b/test/built-ins/Temporal/PlainMonthDay/prototype/with/length.js
new file mode 100644
index 00000000000..ddc88571b0d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/with/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.with
+description: Temporal.PlainMonthDay.prototype.with.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainMonthDay.prototype.with, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/with/name.js b/test/built-ins/Temporal/PlainMonthDay/prototype/with/name.js
new file mode 100644
index 00000000000..207cb15a70b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/with/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.with
+description: Temporal.PlainMonthDay.prototype.with.name is "with".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainMonthDay.prototype.with, "name", {
+ value: "with",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/with/not-a-constructor.js b/test/built-ins/Temporal/PlainMonthDay/prototype/with/not-a-constructor.js
new file mode 100644
index 00000000000..eb83f6c4d83
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/with/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.with
+description: >
+ Temporal.PlainMonthDay.prototype.with does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainMonthDay.prototype.with();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainMonthDay.prototype.with), false,
+ "isConstructor(Temporal.PlainMonthDay.prototype.with)");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/with/options-invalid.js b/test/built-ins/Temporal/PlainMonthDay/prototype/with/options-invalid.js
new file mode 100644
index 00000000000..e26eb9a40bd
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/with/options-invalid.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.with
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(2, 2);
+[null, true, "hello", Symbol("foo"), 1, 1n].forEach((badOptions) =>
+ assert.throws(TypeError, () => instance.with({ day: 17 }, badOptions))
+);
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/with/options-undefined.js b/test/built-ins/Temporal/PlainMonthDay/prototype/with/options-undefined.js
new file mode 100644
index 00000000000..035f174b71a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/with/options-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.with
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const monthday = new Temporal.PlainMonthDay(2, 2);
+const fields = { day: 100 };
+
+const explicit = monthday.with(fields, undefined);
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = monthday.with(fields);
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/with/order-of-operations.js b/test/built-ins/Temporal/PlainMonthDay/prototype/with/order-of-operations.js
new file mode 100644
index 00000000000..7daf9f20b8c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/with/order-of-operations.js
@@ -0,0 +1,51 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.with
+description: Properties on an object passed to with() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(5, 2);
+const expected = [
+ "get calendar",
+ "get timeZone",
+ "get day",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get month",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+];
+const actual = [];
+const fields = {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+ day: 1.7,
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = instance.with(argument);
+TemporalHelpers.assertPlainMonthDay(result, "M01", 1);
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/with/overflow-invalid-string.js b/test/built-ins/Temporal/PlainMonthDay/prototype/with/overflow-invalid-string.js
new file mode 100644
index 00000000000..5ed47e97538
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/with/overflow-invalid-string.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.with
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isomonthdayfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainmonthday.prototype.with step 16:
+ 16. Return ? MonthDayFromFields(_calendar_, _fields_, _options_).
+features: [Temporal]
+---*/
+
+const monthday = new Temporal.PlainMonthDay(5, 2);
+for (const overflow of ["", "CONSTRAIN", "balance", "other string", "constra\u0131n"]) {
+ assert.throws(RangeError, () => monthday.with({ day: 8 }, { overflow }));
+}
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/with/overflow-undefined.js b/test/built-ins/Temporal/PlainMonthDay/prototype/with/overflow-undefined.js
new file mode 100644
index 00000000000..ecc8f7fc66b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/with/overflow-undefined.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.with
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isomonthdayfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainmonthday.prototype.with step 16:
+ 16. Return ? MonthDayFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthday = new Temporal.PlainMonthDay(5, 2);
+const explicit = monthday.with({ day: 33 }, { overflow: undefined });
+TemporalHelpers.assertPlainMonthDay(explicit, "M05", 31, "default overflow is constrain");
+const implicit = monthday.with({ day: 33 }, {});
+TemporalHelpers.assertPlainMonthDay(implicit, "M05", 31, "default overflow is constrain");
+const lambda = monthday.with({ day: 33 }, () => {});
+TemporalHelpers.assertPlainMonthDay(lambda, "M05", 31, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/with/overflow-wrong-type.js b/test/built-ins/Temporal/PlainMonthDay/prototype/with/overflow-wrong-type.js
new file mode 100644
index 00000000000..6fe7d4fb8f7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/with/overflow-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.with
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isomonthdayfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainmonthday.prototype.with step 16:
+ 16. Return ? MonthDayFromFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const monthday = new Temporal.PlainMonthDay(5, 2);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => monthday.with({ day: 8 }, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainMonthDay(result, "M05", 8, descr),
+);
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/with/prop-desc.js b/test/built-ins/Temporal/PlainMonthDay/prototype/with/prop-desc.js
new file mode 100644
index 00000000000..1daa312ca57
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/with/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.with
+description: The "with" property of Temporal.PlainMonthDay.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainMonthDay.prototype.with,
+ "function",
+ "`typeof PlainMonthDay.prototype.with` is `function`"
+);
+
+verifyProperty(Temporal.PlainMonthDay.prototype, "with", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainMonthDay/prototype/with/subclassing-ignored.js b/test/built-ins/Temporal/PlainMonthDay/prototype/with/subclassing-ignored.js
new file mode 100644
index 00000000000..e45e8c77d43
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/prototype/with/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.with
+description: Objects of a subclass are never created as return values for with()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainMonthDay,
+ [5, 2],
+ "with",
+ [{ day: 20 }],
+ (result) => TemporalHelpers.assertPlainMonthDay(result, "M05", 20),
+);
diff --git a/test/built-ins/Temporal/PlainMonthDay/refisoyear-undefined.js b/test/built-ins/Temporal/PlainMonthDay/refisoyear-undefined.js
new file mode 100644
index 00000000000..f9671ba848b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainMonthDay/refisoyear-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday
+description: referenceISOYear argument defaults to 1972 if not given
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const args = [5, 2, calendar];
+
+const dateExplicit = new Temporal.PlainMonthDay(...args, undefined);
+assert.sameValue(dateExplicit.getISOFields().isoYear, 1972, "default referenceISOYear is 1972");
+
+const dateImplicit = new Temporal.PlainMonthDay(...args);
+assert.sameValue(dateImplicit.getISOFields().isoYear, 1972, "default referenceISOYear is 1972");
diff --git a/test/built-ins/Temporal/PlainTime/basic.js b/test/built-ins/Temporal/PlainTime/basic.js
new file mode 100644
index 00000000000..40ea36f208a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/basic.js
@@ -0,0 +1,14 @@
+// Copyright (C) 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);
+assert.sameValue(plainTime.calendar.id, "iso8601", "calendar");
diff --git a/test/built-ins/Temporal/PlainTime/builtin.js b/test/built-ins/Temporal/PlainTime/builtin.js
new file mode 100644
index 00000000000..a17dad6bc08
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/builtin.js
@@ -0,0 +1,27 @@
+// Copyright (C) 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");
diff --git a/test/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-negative-epochnanoseconds.js b/test/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 00000000000..07dadb0d575
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,25 @@
+// Copyright (C) 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 106.
+ 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);
diff --git a/test/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..72c5b1d431f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,18 @@
+// Copyright (C) 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));
+});
diff --git a/test/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..61ef5976d15
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,18 @@
+// Copyright (C) 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));
+});
diff --git a/test/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..eafc91fe901
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,27 @@
+// Copyright (C) 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));
+});
diff --git a/test/built-ins/Temporal/PlainTime/compare/builtin.js b/test/built-ins/Temporal/PlainTime/compare/builtin.js
new file mode 100644
index 00000000000..72266655c80
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/compare/builtin.js
@@ -0,0 +1,30 @@
+// Copyright (C) 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");
diff --git a/test/built-ins/Temporal/PlainTime/compare/calendar-temporal-object.js b/test/built-ins/Temporal/PlainTime/compare/calendar-temporal-object.js
new file mode 100644
index 00000000000..c1eb23e228f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/compare/calendar-temporal-object.js
@@ -0,0 +1,27 @@
+// Copyright (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: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaintime.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalTime(_one_).
+ 2. Set _two_ to ? ToTemporalTime(_two_).
+ sec-temporal-totemporaltime step 3.d:
+ d. If _calendar_ is not *undefined*, then
+ i. Set _calendar_ to ? ToTemporalCalendar(_calendar_).
+ ii. If ? ToString(_calendar_) is not *"iso8601"*, then
+ 1. Throw a *RangeError* exception.
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ assert.throws(RangeError, () => Temporal.PlainTime.compare({ hour: 12, minute: 30, calendar: temporalObject }, time));
+ assert.throws(RangeError, () => Temporal.PlainTime.compare(time, { hour: 12, minute: 30, calendar: temporalObject }));
+});
diff --git a/test/built-ins/Temporal/PlainTime/compare/length.js b/test/built-ins/Temporal/PlainTime/compare/length.js
new file mode 100644
index 00000000000..32981237e30
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/compare/length.js
@@ -0,0 +1,25 @@
+// Copyright (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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/compare/name.js b/test/built-ins/Temporal/PlainTime/compare/name.js
new file mode 100644
index 00000000000..8c9de9b7aec
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/compare/name.js
@@ -0,0 +1,23 @@
+// Copyright (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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/compare/not-a-constructor.js b/test/built-ins/Temporal/PlainTime/compare/not-a-constructor.js
new file mode 100644
index 00000000000..89228d3193d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/compare/not-a-constructor.js
@@ -0,0 +1,20 @@
+// Copyright (C) 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)");
diff --git a/test/built-ins/Temporal/PlainTime/compare/prop-desc.js b/test/built-ins/Temporal/PlainTime/compare/prop-desc.js
new file mode 100644
index 00000000000..f3c91caee11
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/compare/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/compare/use-internal-slots.js b/test/built-ins/Temporal/PlainTime/compare/use-internal-slots.js
new file mode 100644
index 00000000000..6c923979823
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/compare/use-internal-slots.js
@@ -0,0 +1,35 @@
+// Copyright (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);
diff --git a/test/built-ins/Temporal/PlainTime/constructor.js b/test/built-ins/Temporal/PlainTime/constructor.js
new file mode 100644
index 00000000000..819dcba897b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/constructor.js
@@ -0,0 +1,12 @@
+// Copyright (C) 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());
diff --git a/test/built-ins/Temporal/PlainTime/from/argument-string-with-calendar.js b/test/built-ins/Temporal/PlainTime/from/argument-string-with-calendar.js
new file mode 100644
index 00000000000..84bb8ee43ba
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/from/argument-string-with-calendar.js
@@ -0,0 +1,35 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-totemporaltime
+description: Strings with non-ISO calendars are not supported.
+info: |
+ b. Let result be ? ParseTemporalTimeString(string).
+ d. If result.[[Calendar]] is not one of undefined or "iso8601", then
+ i. Throw a RangeError exception.
+features: [Temporal]
+---*/
+
+const isoString = "2004-03-21T10:00:00";
+
+const valid = [
+ "",
+ "[u-ca=iso8601]",
+];
+
+for (const s of valid) {
+ const input = isoString + s;
+ const plainTime = Temporal.PlainTime.from(input);
+ assert.sameValue(plainTime.calendar.id, "iso8601");
+}
+
+const invalid = [
+ "[u-ca=indian]",
+ "[u-ca=hebrew]",
+];
+
+for (const s of invalid) {
+ const input = isoString + s;
+ assert.throws(RangeError, () => Temporal.PlainTime.from(input));
+}
diff --git a/test/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-balance-negative-time-units.js b/test/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 00000000000..6013cb32b71
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,43 @@
+// Copyright (C) 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 = new Temporal.TimeZone("-00:00:00.000000002");
+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);
diff --git a/test/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-negative-epochnanoseconds.js b/test/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 00000000000..a67da3eb4d2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,22 @@
+// Copyright (C) 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 106.
+ 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);
diff --git a/test/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..db1149cc4cd
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 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));
+});
diff --git a/test/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..f9212b45f35
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 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));
+});
diff --git a/test/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..ff20e6fefed
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 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));
+});
diff --git a/test/built-ins/Temporal/PlainTime/from/builtin.js b/test/built-ins/Temporal/PlainTime/from/builtin.js
new file mode 100644
index 00000000000..339f8f8631b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/from/builtin.js
@@ -0,0 +1,30 @@
+// Copyright (C) 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");
diff --git a/test/built-ins/Temporal/PlainTime/from/calendar-temporal-object.js b/test/built-ins/Temporal/PlainTime/from/calendar-temporal-object.js
new file mode 100644
index 00000000000..639a79426a9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/from/calendar-temporal-object.js
@@ -0,0 +1,24 @@
+// Copyright (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: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaintime.from step 4:
+ 4. Return ? ToTemporalTime(_temporalTime_, _overflow_).
+ sec-temporal-totemporaltime step 3.d:
+ d. If _calendar_ is not *undefined*, then
+ i. Set _calendar_ to ? ToTemporalCalendar(_calendar_).
+ ii. If ? ToString(_calendar_) is not *"iso8601"*, then
+ 1. Throw a *RangeError* exception.
+ 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) => {
+ assert.throws(RangeError, () => Temporal.PlainTime.from({ hour: 12, minute: 34, second: 56, calendar: temporalObject }));
+});
diff --git a/test/built-ins/Temporal/PlainTime/from/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainTime/from/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..074e3860292
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/from/infinity-throws-rangeerror.js
@@ -0,0 +1,24 @@
+// 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");
+ });
+ });
+});
diff --git a/test/built-ins/Temporal/PlainTime/from/length.js b/test/built-ins/Temporal/PlainTime/from/length.js
new file mode 100644
index 00000000000..3742bfe96d3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/from/length.js
@@ -0,0 +1,25 @@
+// Copyright (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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/from/name.js b/test/built-ins/Temporal/PlainTime/from/name.js
new file mode 100644
index 00000000000..7033050d2a9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/from/name.js
@@ -0,0 +1,23 @@
+// Copyright (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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/from/not-a-constructor.js b/test/built-ins/Temporal/PlainTime/from/not-a-constructor.js
new file mode 100644
index 00000000000..6dcfaf4b6b7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/from/not-a-constructor.js
@@ -0,0 +1,20 @@
+// Copyright (C) 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)");
diff --git a/test/built-ins/Temporal/PlainTime/from/options-undefined.js b/test/built-ins/Temporal/PlainTime/from/options-undefined.js
new file mode 100644
index 00000000000..060636b2fee
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/from/options-undefined.js
@@ -0,0 +1,16 @@
+// Copyright (C) 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");
diff --git a/test/built-ins/Temporal/PlainTime/from/order-of-operations.js b/test/built-ins/Temporal/PlainTime/from/order-of-operations.js
new file mode 100644
index 00000000000..afcfd14b895
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/from/order-of-operations.js
@@ -0,0 +1,56 @@
+// Copyright (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 calendar",
+ "get hour",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get microsecond",
+ "get microsecond.valueOf",
+ "call microsecond.valueOf",
+ "get millisecond",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf",
+ "get minute",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get nanosecond",
+ "get nanosecond.valueOf",
+ "call nanosecond.valueOf",
+ "get second",
+ "get second.valueOf",
+ "call second.valueOf",
+];
+const actual = [];
+const fields = {
+ hour: 1.7,
+ minute: 1.7,
+ second: 1.7,
+ millisecond: 1.7,
+ microsecond: 1.7,
+ nanosecond: 1.7,
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ if (key === "calendar") return Temporal.Calendar.from("iso8601");
+ const result = target[key];
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = Temporal.PlainTime.from(argument);
+TemporalHelpers.assertPlainTime(result, 1, 1, 1, 1, 1, 1);
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainTime/from/overflow-invalid-string.js b/test/built-ins/Temporal/PlainTime/from/overflow-invalid-string.js
new file mode 100644
index 00000000000..7d974dd5e9c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/from/overflow-invalid-string.js
@@ -0,0 +1,24 @@
+// Copyright (C) 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",
+];
+validValues.forEach((value) => {
+ assert.throws(RangeError, () => Temporal.PlainTime.from(value, { overflow: "other string" }));
+});
diff --git a/test/built-ins/Temporal/PlainTime/from/overflow-undefined.js b/test/built-ins/Temporal/PlainTime/from/overflow-undefined.js
new file mode 100644
index 00000000000..76f2e7d824f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/from/overflow-undefined.js
@@ -0,0 +1,33 @@
+// Copyright (C) 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 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");
diff --git a/test/built-ins/Temporal/PlainTime/from/overflow-wrong-type.js b/test/built-ins/Temporal/PlainTime/from/overflow-wrong-type.js
new file mode 100644
index 00000000000..2eac35a855c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/from/overflow-wrong-type.js
@@ -0,0 +1,26 @@
+// Copyright (C) 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),
+));
diff --git a/test/built-ins/Temporal/PlainTime/from/prop-desc.js b/test/built-ins/Temporal/PlainTime/from/prop-desc.js
new file mode 100644
index 00000000000..f00247f31e6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/from/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/from/subclassing-ignored.js b/test/built-ins/Temporal/PlainTime/from/subclassing-ignored.js
new file mode 100644
index 00000000000..e999e09c5d7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/from/subclassing-ignored.js
@@ -0,0 +1,16 @@
+// Copyright (C) 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),
+);
diff --git a/test/built-ins/Temporal/PlainTime/hour-undefined.js b/test/built-ins/Temporal/PlainTime/hour-undefined.js
new file mode 100644
index 00000000000..a3e31b731f9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/hour-undefined.js
@@ -0,0 +1,15 @@
+// Copyright (C) 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");
diff --git a/test/built-ins/Temporal/PlainTime/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainTime/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..7c3952cbf98
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/infinity-throws-rangeerror.js
@@ -0,0 +1,57 @@
+// 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`);
+}
diff --git a/test/built-ins/Temporal/PlainTime/length.js b/test/built-ins/Temporal/PlainTime/length.js
new file mode 100644
index 00000000000..ac94c418c66
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/microsecond-undefined.js b/test/built-ins/Temporal/PlainTime/microsecond-undefined.js
new file mode 100644
index 00000000000..401c42bb86e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/microsecond-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 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");
diff --git a/test/built-ins/Temporal/PlainTime/millisecond-undefined.js b/test/built-ins/Temporal/PlainTime/millisecond-undefined.js
new file mode 100644
index 00000000000..ce786d2aa23
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/millisecond-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 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");
diff --git a/test/built-ins/Temporal/PlainTime/minute-undefined.js b/test/built-ins/Temporal/PlainTime/minute-undefined.js
new file mode 100644
index 00000000000..36f0f74ab21
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/minute-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 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");
diff --git a/test/built-ins/Temporal/PlainTime/name.js b/test/built-ins/Temporal/PlainTime/name.js
new file mode 100644
index 00000000000..ded7b772e4c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/nanosecond-undefined.js b/test/built-ins/Temporal/PlainTime/nanosecond-undefined.js
new file mode 100644
index 00000000000..827a2bfba8b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/nanosecond-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 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");
diff --git a/test/built-ins/Temporal/PlainTime/negative-infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainTime/negative-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..42221addc15
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,57 @@
+// 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`);
+}
diff --git a/test/built-ins/Temporal/PlainTime/prop-desc.js b/test/built-ins/Temporal/PlainTime/prop-desc.js
new file mode 100644
index 00000000000..99ba6899789
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/add/argument-not-object.js b/test/built-ins/Temporal/PlainTime/prototype/add/argument-not-object.js
new file mode 100644
index 00000000000..d9680e852ce
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/add/argument-not-object.js
@@ -0,0 +1,17 @@
+// Copyright (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 = Temporal.PlainTime.from({ hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+assert.throws(RangeError, () => instance.add(undefined), "undefined");
+assert.throws(RangeError, () => instance.add(null), "null");
+assert.throws(RangeError, () => instance.add(true), "boolean");
+assert.throws(RangeError, () => instance.add(""), "empty string");
+assert.throws(TypeError, () => instance.add(Symbol()), "Symbol");
+assert.throws(RangeError, () => instance.add(7), "number");
+assert.throws(RangeError, () => instance.add(7n), "bigint");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/add/argument-string-negative-fractional-units.js b/test/built-ins/Temporal/PlainTime/prototype/add/argument-string-negative-fractional-units.js
new file mode 100644
index 00000000000..81a981bd6e5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/add/argument-string-negative-fractional-units.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 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, 201, "negative fractional hours");
+
+const resultMinutes = instance.add("-PT1440.567890123M");
+TemporalHelpers.assertPlainTime(resultMinutes, 23, 59, 25, 926, 592, 621, "negative fractional minutes");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/add/argument-string.js b/test/built-ins/Temporal/PlainTime/prototype/add/argument-string.js
new file mode 100644
index 00000000000..2e4b12a51a9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/add/argument-string.js
@@ -0,0 +1,13 @@
+// Copyright (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);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/add/balance-negative-time-units.js b/test/built-ins/Temporal/PlainTime/prototype/add/balance-negative-time-units.js
new file mode 100644
index 00000000000..c21c8c01ca8
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/add/balance-negative-time-units.js
@@ -0,0 +1,47 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/add/builtin.js b/test/built-ins/Temporal/PlainTime/prototype/add/builtin.js
new file mode 100644
index 00000000000..5701cf749a7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/add/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/add/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainTime/prototype/add/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..cbffd182253
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/add/infinity-throws-rangeerror.js
@@ -0,0 +1,35 @@
+// 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");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/add/length.js b/test/built-ins/Temporal/PlainTime/prototype/add/length.js
new file mode 100644
index 00000000000..9c20a771c00
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/add/length.js
@@ -0,0 +1,25 @@
+// Copyright (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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/add/name.js b/test/built-ins/Temporal/PlainTime/prototype/add/name.js
new file mode 100644
index 00000000000..9a882f93898
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/add/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/add/negative-infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainTime/prototype/add/negative-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..3c88355789c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/add/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,35 @@
+// 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");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/add/non-integer-throws-rangeerror.js b/test/built-ins/Temporal/PlainTime/prototype/add/non-integer-throws-rangeerror.js
new file mode 100644
index 00000000000..9ad3acf10a1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/add/non-integer-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 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 }));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/add/not-a-constructor.js b/test/built-ins/Temporal/PlainTime/prototype/add/not-a-constructor.js
new file mode 100644
index 00000000000..f7fe58e9f82
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/add/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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)");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/add/order-of-operations.js b/test/built-ins/Temporal/PlainTime/prototype/add/order-of-operations.js
new file mode 100644
index 00000000000..ee1993f3898
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/add/order-of-operations.js
@@ -0,0 +1,74 @@
+// Copyright (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 days",
+ "get days.valueOf",
+ "call days.valueOf",
+ "get hours",
+ "get hours.valueOf",
+ "call hours.valueOf",
+ "get microseconds",
+ "get microseconds.valueOf",
+ "call microseconds.valueOf",
+ "get milliseconds",
+ "get milliseconds.valueOf",
+ "call milliseconds.valueOf",
+ "get minutes",
+ "get minutes.valueOf",
+ "call minutes.valueOf",
+ "get months",
+ "get months.valueOf",
+ "call months.valueOf",
+ "get nanoseconds",
+ "get nanoseconds.valueOf",
+ "call nanoseconds.valueOf",
+ "get seconds",
+ "get seconds.valueOf",
+ "call seconds.valueOf",
+ "get weeks",
+ "get weeks.valueOf",
+ "call weeks.valueOf",
+ "get years",
+ "get years.valueOf",
+ "call years.valueOf",
+];
+const actual = [];
+const fields = {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = instance.add(argument);
+TemporalHelpers.assertPlainTime(result, 13, 35, 57, 988, 655, 322);
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/add/prop-desc.js b/test/built-ins/Temporal/PlainTime/prototype/add/prop-desc.js
new file mode 100644
index 00000000000..274aaea8161
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/add/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/add/subclassing-ignored.js b/test/built-ins/Temporal/PlainTime/prototype/add/subclassing-ignored.js
new file mode 100644
index 00000000000..11c68bfe234
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/add/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 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),
+);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/calendar/prop-desc.js b/test/built-ins/Temporal/PlainTime/prototype/calendar/prop-desc.js
new file mode 100644
index 00000000000..d4ffd5312eb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/calendar/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 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.calendar
+description: The "calendar" property of Temporal.PlainTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainTime.prototype, "calendar");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/equals/argument-wrong-type.js b/test/built-ins/Temporal/PlainTime/prototype/equals/argument-wrong-type.js
new file mode 100644
index 00000000000..d01e485613a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/equals/argument-wrong-type.js
@@ -0,0 +1,20 @@
+// Copyright (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: Appropriate error thrown when argument cannot be converted to a valid string
+features: [Symbol, Temporal]
+---*/
+
+const instance = Temporal.PlainTime.from({ minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+
+assert.throws(RangeError, () => instance.equals(undefined), "undefined");
+assert.throws(RangeError, () => instance.equals(null), "null");
+assert.throws(RangeError, () => instance.equals(true), "true");
+assert.throws(RangeError, () => instance.equals(""), "empty string");
+assert.throws(TypeError, () => instance.equals(Symbol()), "symbol");
+assert.throws(RangeError, () => instance.equals(1), "1");
+assert.throws(TypeError, () => instance.equals({}), "plain object");
+assert.throws(TypeError, () => instance.equals(Temporal.PlainTime), "Temporal.PlainTime");
+assert.throws(TypeError, () => instance.equals(Temporal.PlainTime.prototype), "Temporal.PlainTime.prototype");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js b/test/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 00000000000..75db40bce71
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,40 @@
+// Copyright (C) 2021 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_).
+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 = new Temporal.TimeZone("-00:00:00.000000002");
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+assert(new Temporal.PlainTime(1, 1, 1, 1, 0, 999).equals(datetime));
diff --git a/test/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js b/test/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 00000000000..f815332b38a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 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 106.
+ 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);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..a903404fcd3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 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));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..a7545c0a200
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 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_001, 86400_000_000_001].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));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..be2ce0f1adc
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 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));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/equals/builtin.js b/test/built-ins/Temporal/PlainTime/prototype/equals/builtin.js
new file mode 100644
index 00000000000..95b80823d08
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/equals/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/equals/calendar-temporal-object.js b/test/built-ins/Temporal/PlainTime/prototype/equals/calendar-temporal-object.js
new file mode 100644
index 00000000000..0234fb5444f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/equals/calendar-temporal-object.js
@@ -0,0 +1,25 @@
+// Copyright (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: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaintime.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalTime(_other_).
+ sec-temporal-totemporaltime step 3.d:
+ d. If _calendar_ is not *undefined*, then
+ i. Set _calendar_ to ? ToTemporalCalendar(_calendar_).
+ ii. If ? ToString(_calendar_) is not *"iso8601"*, then
+ 1. Throw a *RangeError* exception.
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ assert.throws(RangeError, () => time.equals({ hour: 12, minute: 30, calendar: temporalObject }));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/equals/length.js b/test/built-ins/Temporal/PlainTime/prototype/equals/length.js
new file mode 100644
index 00000000000..8df59ba5eec
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/equals/length.js
@@ -0,0 +1,25 @@
+// Copyright (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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/equals/name.js b/test/built-ins/Temporal/PlainTime/prototype/equals/name.js
new file mode 100644
index 00000000000..0363131f134
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/equals/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/equals/not-a-constructor.js b/test/built-ins/Temporal/PlainTime/prototype/equals/not-a-constructor.js
new file mode 100644
index 00000000000..ed4b817e8d1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/equals/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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)");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/equals/prop-desc.js b/test/built-ins/Temporal/PlainTime/prototype/equals/prop-desc.js
new file mode 100644
index 00000000000..7a99062d515
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/equals/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/getISOFields/builtin.js b/test/built-ins/Temporal/PlainTime/prototype/getISOFields/builtin.js
new file mode 100644
index 00000000000..352d8b55c0f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/getISOFields/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/getISOFields/field-names.js b/test/built-ins/Temporal/PlainTime/prototype/getISOFields/field-names.js
new file mode 100644
index 00000000000..df16645aa97
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/getISOFields/field-names.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 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");
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/getISOFields/field-prop-desc.js b/test/built-ins/Temporal/PlainTime/prototype/getISOFields/field-prop-desc.js
new file mode 100644
index 00000000000..3d4c7a6cb5c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/getISOFields/field-prop-desc.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 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 = [
+ "calendar",
+ "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,
+ });
+}
diff --git a/test/built-ins/Temporal/PlainTime/prototype/getISOFields/field-traversal-order.js b/test/built-ins/Temporal/PlainTime/prototype/getISOFields/field-traversal-order.js
new file mode 100644
index 00000000000..882bfda76ea
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/getISOFields/field-traversal-order.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 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 = [
+ "calendar",
+ "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);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/getISOFields/length.js b/test/built-ins/Temporal/PlainTime/prototype/getISOFields/length.js
new file mode 100644
index 00000000000..8e71933c384
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/getISOFields/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/getISOFields/name.js b/test/built-ins/Temporal/PlainTime/prototype/getISOFields/name.js
new file mode 100644
index 00000000000..9ad9e6438b5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/getISOFields/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/getISOFields/not-a-constructor.js b/test/built-ins/Temporal/PlainTime/prototype/getISOFields/not-a-constructor.js
new file mode 100644
index 00000000000..1160d5f9e1f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/getISOFields/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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)");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/getISOFields/prop-desc.js b/test/built-ins/Temporal/PlainTime/prototype/getISOFields/prop-desc.js
new file mode 100644
index 00000000000..534c3d01721
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/getISOFields/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/hour/prop-desc.js b/test/built-ins/Temporal/PlainTime/prototype/hour/prop-desc.js
new file mode 100644
index 00000000000..48cab0f78b0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/hour/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 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);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/microsecond/prop-desc.js b/test/built-ins/Temporal/PlainTime/prototype/microsecond/prop-desc.js
new file mode 100644
index 00000000000..6259a087779
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/microsecond/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 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);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/millisecond/prop-desc.js b/test/built-ins/Temporal/PlainTime/prototype/millisecond/prop-desc.js
new file mode 100644
index 00000000000..7da8095fcf8
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/millisecond/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 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);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/minute/prop-desc.js b/test/built-ins/Temporal/PlainTime/prototype/minute/prop-desc.js
new file mode 100644
index 00000000000..1d02d79a8bd
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/minute/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 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);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/nanosecond/prop-desc.js b/test/built-ins/Temporal/PlainTime/prototype/nanosecond/prop-desc.js
new file mode 100644
index 00000000000..f076273e157
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/nanosecond/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 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);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/round/builtin.js b/test/built-ins/Temporal/PlainTime/prototype/round/builtin.js
new file mode 100644
index 00000000000..b54eda61a1c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/round/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/round/length.js b/test/built-ins/Temporal/PlainTime/prototype/round/length.js
new file mode 100644
index 00000000000..0232e328a26
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/round/length.js
@@ -0,0 +1,25 @@
+// Copyright (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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/round/name.js b/test/built-ins/Temporal/PlainTime/prototype/round/name.js
new file mode 100644
index 00000000000..e535cea8d87
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/round/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/round/not-a-constructor.js b/test/built-ins/Temporal/PlainTime/prototype/round/not-a-constructor.js
new file mode 100644
index 00000000000..65abb6eaead
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/round/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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)");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/round/prop-desc.js b/test/built-ins/Temporal/PlainTime/prototype/round/prop-desc.js
new file mode 100644
index 00000000000..cf3667adff1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/round/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-nan.js b/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-nan.js
new file mode 100644
index 00000000000..c292460f390
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-nan.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 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 }));
diff --git a/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-non-integer.js b/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-non-integer.js
new file mode 100644
index 00000000000..fcee98cb7b4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 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: |
+ sec-temporal-totemporalroundingincrement step 7:
+ 7. Set _increment_ to floor(ℝ(_increment_)).
+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 });
+TemporalHelpers.assertPlainTime(result, 12, 34, 56, 0, 0, 6, "roundingIncrement 2.5 floors to 2");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-out-of-range.js b/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-out-of-range.js
new file mode 100644
index 00000000000..fb0493f2056
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-out-of-range.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 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: |
+ sec-temporal-totemporalroundingincrement step 6:
+ 6. If _increment_ < 1 or _increment_ > _maximum_, throw a *RangeError* exception.
+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: Infinity }));
diff --git a/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-undefined.js b/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-undefined.js
new file mode 100644
index 00000000000..8cb695450e7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-undefined.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-wrong-type.js b/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-wrong-type.js
new file mode 100644
index 00000000000..ebd59bb7995
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 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),
+);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/round/roundingmode-invalid-string.js b/test/built-ins/Temporal/PlainTime/prototype/round/roundingmode-invalid-string.js
new file mode 100644
index 00000000000..8048aab5b55
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/round/roundingmode-invalid-string.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 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);
+assert.throws(RangeError, () => time.round({ smallestUnit: "microsecond", roundingMode: "other string" }));
diff --git a/test/built-ins/Temporal/PlainTime/prototype/round/roundingmode-undefined.js b/test/built-ins/Temporal/PlainTime/prototype/round/roundingmode-undefined.js
new file mode 100644
index 00000000000..9e02425c01e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/round/roundingmode-undefined.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 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 roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
+
+const explicit1 = time.round({ smallestUnit: "microsecond", roundingMode: undefined });
+TemporalHelpers.assertPlainTime(explicit1, 12, 34, 56, 123, 988, 0, "default roundingMode is halfExpand");
+const implicit1 = time.round({ smallestUnit: "microsecond" });
+TemporalHelpers.assertPlainTime(implicit1, 12, 34, 56, 123, 988, 0, "default roundingMode is halfExpand");
+
+const explicit2 = time.round({ smallestUnit: "millisecond", roundingMode: undefined });
+TemporalHelpers.assertPlainTime(explicit2, 12, 34, 56, 124, 0, 0, "default roundingMode is halfExpand");
+const implicit2 = time.round({ smallestUnit: "millisecond" });
+TemporalHelpers.assertPlainTime(implicit2, 12, 34, 56, 124, 0, 0, "default roundingMode is halfExpand");
+
+const explicit3 = time.round({ smallestUnit: "second", roundingMode: undefined });
+TemporalHelpers.assertPlainTime(explicit3, 12, 34, 56, 0, 0, 0, "default roundingMode is halfExpand");
+const implicit3 = time.round({ smallestUnit: "second" });
+TemporalHelpers.assertPlainTime(implicit3, 12, 34, 56, 0, 0, 0, "default roundingMode is halfExpand");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/round/roundingmode-wrong-type.js b/test/built-ins/Temporal/PlainTime/prototype/round/roundingmode-wrong-type.js
new file mode 100644
index 00000000000..74311402cb8
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/round/roundingmode-wrong-type.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 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),
+);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/round/smallestunit-invalid-string.js b/test/built-ins/Temporal/PlainTime/prototype/round/smallestunit-invalid-string.js
new file mode 100644
index 00000000000..71a8d55f25d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/round/smallestunit-invalid-string.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 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);
+assert.throws(RangeError, () => time.round({ smallestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/PlainTime/prototype/round/smallestunit-plurals-accepted.js b/test/built-ins/Temporal/PlainTime/prototype/round/smallestunit-plurals-accepted.js
new file mode 100644
index 00000000000..32ab2bbfee5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/round/smallestunit-plurals-accepted.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 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]
+---*/
+
+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);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/round/smallestunit-wrong-type.js b/test/built-ins/Temporal/PlainTime/prototype/round/smallestunit-wrong-type.js
new file mode 100644
index 00000000000..b1eb4b1e2f8
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/round/smallestunit-wrong-type.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 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),
+);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/round/subclassing-ignored.js b/test/built-ins/Temporal/PlainTime/prototype/round/subclassing-ignored.js
new file mode 100644
index 00000000000..2cb25eca98b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/round/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 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),
+);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/second/prop-desc.js b/test/built-ins/Temporal/PlainTime/prototype/second/prop-desc.js
new file mode 100644
index 00000000000..89566763a19
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/second/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 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);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-balance-negative-time-units.js b/test/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 00000000000..e1ab14b1500
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 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 = new Temporal.TimeZone("-00:00:00.000000002");
+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);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-negative-epochnanoseconds.js b/test/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 00000000000..815dbfc9f26
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 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 106.
+ 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);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..c8230cbea46
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 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));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..af347d52ba0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 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_001, 86400_000_000_001].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));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..0f0a0686870
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 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));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/balance-negative-time-units.js b/test/built-ins/Temporal/PlainTime/prototype/since/balance-negative-time-units.js
new file mode 100644
index 00000000000..06e8e8140e7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/balance-negative-time-units.js
@@ -0,0 +1,48 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/builtin.js b/test/built-ins/Temporal/PlainTime/prototype/since/builtin.js
new file mode 100644
index 00000000000..6b123454a32
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/calendar-temporal-object.js b/test/built-ins/Temporal/PlainTime/prototype/since/calendar-temporal-object.js
new file mode 100644
index 00000000000..04bf4d7492d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/calendar-temporal-object.js
@@ -0,0 +1,25 @@
+// Copyright (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: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaintime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalTime(_other_).
+ sec-temporal-totemporaltime step 3.d:
+ d. If _calendar_ is not *undefined*, then
+ i. Set _calendar_ to ? ToTemporalCalendar(_calendar_).
+ ii. If ? ToString(_calendar_) is not *"iso8601"*, then
+ 1. Throw a *RangeError* exception.
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ assert.throws(RangeError, () => time.since({ hour: 12, minute: 30, calendar: temporalObject }));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/largestunit-invalid-string.js b/test/built-ins/Temporal/PlainTime/prototype/since/largestunit-invalid-string.js
new file mode 100644
index 00000000000..4a7de90c931
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/largestunit-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 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);
+assert.throws(RangeError, () => later.since(earlier, { largestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/largestunit-plurals-accepted.js b/test/built-ins/Temporal/PlainTime/prototype/since/largestunit-plurals-accepted.js
new file mode 100644
index 00000000000..4a8fe1df48a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/largestunit-plurals-accepted.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/largestunit-undefined.js b/test/built-ins/Temporal/PlainTime/prototype/since/largestunit-undefined.js
new file mode 100644
index 00000000000..2893685f290
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/largestunit-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/largestunit-wrong-type.js b/test/built-ins/Temporal/PlainTime/prototype/since/largestunit-wrong-type.js
new file mode 100644
index 00000000000..d499ddf9197
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/largestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 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),
+);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/length.js b/test/built-ins/Temporal/PlainTime/prototype/since/length.js
new file mode 100644
index 00000000000..c908634baea
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/length.js
@@ -0,0 +1,25 @@
+// Copyright (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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/name.js b/test/built-ins/Temporal/PlainTime/prototype/since/name.js
new file mode 100644
index 00000000000..16c2d967ddd
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/not-a-constructor.js b/test/built-ins/Temporal/PlainTime/prototype/since/not-a-constructor.js
new file mode 100644
index 00000000000..eb8d40cc1d5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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)");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/options-undefined.js b/test/built-ins/Temporal/PlainTime/prototype/since/options-undefined.js
new file mode 100644
index 00000000000..5971f0246f4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/options-undefined.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/prop-desc.js b/test/built-ins/Temporal/PlainTime/prototype/since/prop-desc.js
new file mode 100644
index 00000000000..dc5dbda78b3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-nan.js b/test/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-nan.js
new file mode 100644
index 00000000000..2c045806781
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-nan.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 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 }));
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-non-integer.js b/test/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-non-integer.js
new file mode 100644
index 00000000000..2795f426db5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-non-integer.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 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: |
+ sec-temporal-totemporalroundingincrement step 7:
+ 7. Set _increment_ to floor(ℝ(_increment_)).
+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 });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, "roundingIncrement 2.5 floors to 2");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-out-of-range.js b/test/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-out-of-range.js
new file mode 100644
index 00000000000..6e3d7a1c8bd
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-out-of-range.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 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: |
+ sec-temporal-totemporalroundingincrement step 6:
+ 6. If _increment_ < 1 or _increment_ > _maximum_, throw a *RangeError* exception.
+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: Infinity }));
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-undefined.js b/test/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-undefined.js
new file mode 100644
index 00000000000..8dcd5c2bea4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-wrong-type.js b/test/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-wrong-type.js
new file mode 100644
index 00000000000..6db72c5dc30
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 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),
+);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/roundingmode-invalid-string.js b/test/built-ins/Temporal/PlainTime/prototype/since/roundingmode-invalid-string.js
new file mode 100644
index 00000000000..9778f33342b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/roundingmode-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 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);
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "microsecond", roundingMode: "other string" }));
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/roundingmode-undefined.js b/test/built-ins/Temporal/PlainTime/prototype/since/roundingmode-undefined.js
new file mode 100644
index 00000000000..91eaf058416
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/roundingmode-undefined.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 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 = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 123, 987, 500);
+
+const explicit1 = later.since(earlier, { smallestUnit: "microsecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 0, 1, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+const implicit1 = later.since(earlier, { smallestUnit: "microsecond" });
+TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 0, 1, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+
+const explicit2 = later.since(earlier, { smallestUnit: "millisecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 0, 1, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+const implicit2 = later.since(earlier, { smallestUnit: "millisecond" });
+TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 0, 1, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+
+const explicit3 = later.since(earlier, { smallestUnit: "second", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit3, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, "default roundingMode is trunc");
+const implicit3 = later.since(earlier, { smallestUnit: "second" });
+TemporalHelpers.assertDuration(implicit3, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, "default roundingMode is trunc");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/roundingmode-wrong-type.js b/test/built-ins/Temporal/PlainTime/prototype/since/roundingmode-wrong-type.js
new file mode 100644
index 00000000000..a7289d534d5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/roundingmode-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 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),
+);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/smallestunit-invalid-string.js b/test/built-ins/Temporal/PlainTime/prototype/since/smallestunit-invalid-string.js
new file mode 100644
index 00000000000..100e9957a08
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/smallestunit-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 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);
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/smallestunit-plurals-accepted.js b/test/built-ins/Temporal/PlainTime/prototype/since/smallestunit-plurals-accepted.js
new file mode 100644
index 00000000000..afbc7ca7655
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/smallestunit-plurals-accepted.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/smallestunit-undefined.js b/test/built-ins/Temporal/PlainTime/prototype/since/smallestunit-undefined.js
new file mode 100644
index 00000000000..c28576d54c6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/smallestunit-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/since/smallestunit-wrong-type.js b/test/built-ins/Temporal/PlainTime/prototype/since/smallestunit-wrong-type.js
new file mode 100644
index 00000000000..35292e3667d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/since/smallestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 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),
+);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-not-object.js b/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-not-object.js
new file mode 100644
index 00000000000..f8ea166d1dc
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-not-object.js
@@ -0,0 +1,17 @@
+// Copyright (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 = Temporal.PlainTime.from({ hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
+assert.throws(RangeError, () => instance.subtract(undefined), "undefined");
+assert.throws(RangeError, () => instance.subtract(null), "null");
+assert.throws(RangeError, () => instance.subtract(true), "boolean");
+assert.throws(RangeError, () => instance.subtract(""), "empty string");
+assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol");
+assert.throws(RangeError, () => instance.subtract(7), "number");
+assert.throws(RangeError, () => instance.subtract(7n), "bigint");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-negative-fractional-units.js b/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-negative-fractional-units.js
new file mode 100644
index 00000000000..6ccef17a29d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-negative-fractional-units.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 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, 799, "negative fractional hours");
+
+const resultMinutes = instance.subtract("-PT1440.567890123M");
+TemporalHelpers.assertPlainTime(resultMinutes, 0, 0, 34, 73, 407, 379, "negative fractional minutes");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-string.js b/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-string.js
new file mode 100644
index 00000000000..173b93a6aeb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-string.js
@@ -0,0 +1,13 @@
+// Copyright (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);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/subtract/balance-negative-time-units.js b/test/built-ins/Temporal/PlainTime/prototype/subtract/balance-negative-time-units.js
new file mode 100644
index 00000000000..be65da51550
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/subtract/balance-negative-time-units.js
@@ -0,0 +1,47 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/subtract/builtin.js b/test/built-ins/Temporal/PlainTime/prototype/subtract/builtin.js
new file mode 100644
index 00000000000..f8b1d93ccf0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/subtract/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/subtract/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainTime/prototype/subtract/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..a1aa1c47a45
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/subtract/infinity-throws-rangeerror.js
@@ -0,0 +1,35 @@
+// 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");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/subtract/length.js b/test/built-ins/Temporal/PlainTime/prototype/subtract/length.js
new file mode 100644
index 00000000000..9c28212c186
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/subtract/length.js
@@ -0,0 +1,25 @@
+// Copyright (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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/subtract/name.js b/test/built-ins/Temporal/PlainTime/prototype/subtract/name.js
new file mode 100644
index 00000000000..f8fa13204f0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/subtract/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/subtract/negative-infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainTime/prototype/subtract/negative-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..ebd611101f4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/subtract/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,35 @@
+// 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");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/subtract/non-integer-throws-rangeerror.js b/test/built-ins/Temporal/PlainTime/prototype/subtract/non-integer-throws-rangeerror.js
new file mode 100644
index 00000000000..8c121696283
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/subtract/non-integer-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 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 }));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/subtract/not-a-constructor.js b/test/built-ins/Temporal/PlainTime/prototype/subtract/not-a-constructor.js
new file mode 100644
index 00000000000..dc5130b40e5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/subtract/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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)");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/subtract/order-of-operations.js b/test/built-ins/Temporal/PlainTime/prototype/subtract/order-of-operations.js
new file mode 100644
index 00000000000..789f70df525
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/subtract/order-of-operations.js
@@ -0,0 +1,74 @@
+// Copyright (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 days",
+ "get days.valueOf",
+ "call days.valueOf",
+ "get hours",
+ "get hours.valueOf",
+ "call hours.valueOf",
+ "get microseconds",
+ "get microseconds.valueOf",
+ "call microseconds.valueOf",
+ "get milliseconds",
+ "get milliseconds.valueOf",
+ "call milliseconds.valueOf",
+ "get minutes",
+ "get minutes.valueOf",
+ "call minutes.valueOf",
+ "get months",
+ "get months.valueOf",
+ "call months.valueOf",
+ "get nanoseconds",
+ "get nanoseconds.valueOf",
+ "call nanoseconds.valueOf",
+ "get seconds",
+ "get seconds.valueOf",
+ "call seconds.valueOf",
+ "get weeks",
+ "get weeks.valueOf",
+ "call weeks.valueOf",
+ "get years",
+ "get years.valueOf",
+ "call years.valueOf",
+];
+const actual = [];
+const fields = {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = instance.subtract(argument);
+TemporalHelpers.assertPlainTime(result, 11, 33, 55, 986, 653, 320);
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/subtract/prop-desc.js b/test/built-ins/Temporal/PlainTime/prototype/subtract/prop-desc.js
new file mode 100644
index 00000000000..56a2821980e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/subtract/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/subtract/subclassing-ignored.js b/test/built-ins/Temporal/PlainTime/prototype/subtract/subclassing-ignored.js
new file mode 100644
index 00000000000..66df1a958b9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/subtract/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 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),
+);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toJSON/builtin.js b/test/built-ins/Temporal/PlainTime/prototype/toJSON/builtin.js
new file mode 100644
index 00000000000..a4fa6f763d9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toJSON/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toJSON/length.js b/test/built-ins/Temporal/PlainTime/prototype/toJSON/length.js
new file mode 100644
index 00000000000..db86c204993
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toJSON/length.js
@@ -0,0 +1,25 @@
+// Copyright (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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toJSON/name.js b/test/built-ins/Temporal/PlainTime/prototype/toJSON/name.js
new file mode 100644
index 00000000000..5c5eb807e59
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toJSON/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toJSON/not-a-constructor.js b/test/built-ins/Temporal/PlainTime/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 00000000000..4a890bdea4f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toJSON/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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)");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toJSON/prop-desc.js b/test/built-ins/Temporal/PlainTime/prototype/toJSON/prop-desc.js
new file mode 100644
index 00000000000..962b4500950
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toJSON/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/builtin.js b/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/builtin.js
new file mode 100644
index 00000000000..4ec490c27c8
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/length.js b/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/length.js
new file mode 100644
index 00000000000..73dc66e495f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/length.js
@@ -0,0 +1,25 @@
+// Copyright (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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/locales-undefined.js b/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/locales-undefined.js
new file mode 100644
index 00000000000..cbf18f78813
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/locales-undefined.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 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: Omitting the locales argument defaults to the DateTimeFormat default
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const defaultFormatter = new Intl.DateTimeFormat([], Object.create(null));
+const expected = defaultFormatter.format(time);
+
+const actualExplicit = time.toLocaleString(undefined);
+assert.sameValue(actualExplicit, expected, "default locale is determined by Intl.DateTimeFormat");
+
+const actualImplicit = time.toLocaleString();
+assert.sameValue(actualImplicit, expected, "default locale is determined by Intl.DateTimeFormat");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/name.js b/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/name.js
new file mode 100644
index 00000000000..fe49689fc14
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/not-a-constructor.js b/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/not-a-constructor.js
new file mode 100644
index 00000000000..351b7e6077a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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)");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/options-conflict.js b/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/options-conflict.js
new file mode 100644
index 00000000000..3bc613c8f69
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/options-conflict.js
@@ -0,0 +1,37 @@
+// Copyright (C) 2021 Kate Miháliková. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sup-temporal.plaintime.prototype.tolocalestring
+description: >
+ Conflicting properties of dateStyle must be rejected with a TypeError for the options argument
+info: |
+ Using sec-temporal-getdatetimeformatpattern:
+ GetDateTimeFormatPattern ( dateStyle, timeStyle, matcher, opt, dataLocaleData, hc )
+
+ 1. If dateStyle is not undefined or timeStyle is not undefined, then
+ a. For each row in Table 7, except the header row, do
+ i. Let prop be the name given in the Property column of the row.
+ ii. Let p be opt.[[]].
+ iii. If p is not undefined, then
+ 1. Throw a TypeError exception.
+features: [Temporal]
+---*/
+
+// Table 14 - Supported fields + example value for each field
+const conflictingOptions = [
+ [ "hour", "numeric" ],
+ [ "minute", "numeric" ],
+ [ "second", "numeric" ],
+ [ "dayPeriod", "short" ],
+ [ "fractionalSecondDigits", 3 ],
+];
+const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+assert.sameValue(typeof time.toLocaleString("en", { timeStyle: "short" }), "string");
+
+for (const [ option, value ] of conflictingOptions) {
+ assert.throws(TypeError, function() {
+ time.toLocaleString("en", { [option]: value, timeStyle: "short" });
+ }, `time.toLocaleString("en", { ${option}: "${value}", timeStyle: "short" }) throws TypeError`);
+}
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/options-undefined.js b/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/options-undefined.js
new file mode 100644
index 00000000000..03c98be9daa
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/options-undefined.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 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: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const defaultFormatter = new Intl.DateTimeFormat('en', Object.create(null));
+const expected = defaultFormatter.format(time);
+
+const actualExplicit = time.toLocaleString('en', undefined);
+assert.sameValue(actualExplicit, expected, "default options are determined by Intl.DateTimeFormat");
+
+const actualImplicit = time.toLocaleString('en');
+assert.sameValue(actualImplicit, expected, "default options are determined by Intl.DateTimeFormat");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/prop-desc.js b/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/prop-desc.js
new file mode 100644
index 00000000000..d025d1b1c21
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toLocaleString/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-plaindatetime.js b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-plaindatetime.js
new file mode 100644
index 00000000000..6ea0d4cc4e2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-plaindatetime.js
@@ -0,0 +1,21 @@
+// Copyright (C) 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);
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..0ca2efd3bd8
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 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));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..f8a337841ee
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 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_001, 86400_000_000_001].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));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..99bde536c2f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 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));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/basic.js b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/basic.js
new file mode 100644
index 00000000000..500a3e891fd
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/basic.js
@@ -0,0 +1,22 @@
+// Copyright (C) 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/builtin.js b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/builtin.js
new file mode 100644
index 00000000000..25f4d61902a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-fields-iterable.js
new file mode 100644
index 00000000000..b7f5e7bee7f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-fields-iterable.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-temporal-object.js b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-temporal-object.js
new file mode 100644
index 00000000000..c165f41d6df
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/calendar-temporal-object.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 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.calendar, calendar, "Temporal object coerced to calendar");
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..95ed40d0eee
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// 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");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/length.js b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/length.js
new file mode 100644
index 00000000000..151a4b4259f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/length.js
@@ -0,0 +1,25 @@
+// Copyright (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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/name.js b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/name.js
new file mode 100644
index 00000000000..0096b44aff3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/not-a-constructor.js b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/not-a-constructor.js
new file mode 100644
index 00000000000..e6ccf089c88
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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)");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/prop-desc.js b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/prop-desc.js
new file mode 100644
index 00000000000..6258633f334
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toString/builtin.js b/test/built-ins/Temporal/PlainTime/prototype/toString/builtin.js
new file mode 100644
index 00000000000..878b55efafc
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toString/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-invalid-string.js b/test/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-invalid-string.js
new file mode 100644
index 00000000000..2ab8852f59f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-invalid-string.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 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 ? ToDurationSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 650, 0);
+
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: "other string" }));
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-nan.js b/test/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-nan.js
new file mode 100644
index 00000000000..0dcdd449819
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-nan.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 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 }));
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-non-integer.js b/test/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-non-integer.js
new file mode 100644
index 00000000000..9b7c5250e0d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-non-integer.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 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 ? ToDurationSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 650, 0);
+
+const string = time.toString({ fractionalSecondDigits: 2.5 });
+assert.sameValue(string, "12:34:56.98", "fractionalSecondDigits 2.5 floors to 2");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-out-of-range.js b/test/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-out-of-range.js
new file mode 100644
index 00000000000..d9b2f9c2ae2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-out-of-range.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 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 ? ToDurationSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 650, 0);
+
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: -1 }));
+assert.throws(RangeError, () => time.toString({ fractionalSecondDigits: 10 }));
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-undefined.js b/test/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-undefined.js
new file mode 100644
index 00000000000..f4d320b047a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 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_, *"stringOrNumber"*, *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 ? ToDurationSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(12, 34, 56, 987, 650, 0);
+
+const explicit = time.toString({ fractionalSecondDigits: undefined });
+assert.sameValue(explicit, "12:34:56.98765", "default fractionalSecondDigits is auto");
+
+const implicit = time.toString({});
+assert.sameValue(implicit, "12:34:56.98765", "default fractionalSecondDigits is auto");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-wrong-type.js b/test/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-wrong-type.js
new file mode 100644
index 00000000000..54440155aa3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toString/fractionalseconddigits-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 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);
+TemporalHelpers.checkFractionalSecondDigitsOptionWrongType(time);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toString/length.js b/test/built-ins/Temporal/PlainTime/prototype/toString/length.js
new file mode 100644
index 00000000000..a722f58ab5b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toString/length.js
@@ -0,0 +1,25 @@
+// Copyright (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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toString/name.js b/test/built-ins/Temporal/PlainTime/prototype/toString/name.js
new file mode 100644
index 00000000000..064547137f4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toString/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toString/not-a-constructor.js b/test/built-ins/Temporal/PlainTime/prototype/toString/not-a-constructor.js
new file mode 100644
index 00000000000..d919698725e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toString/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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)");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toString/options-undefined.js b/test/built-ins/Temporal/PlainTime/prototype/toString/options-undefined.js
new file mode 100644
index 00000000000..9299685b318
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toString/options-undefined.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 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 time = new Temporal.PlainTime(12, 34, 56, 987, 650);
+const expected = "12:34:56.98765";
+
+const explicit = time.toString(undefined);
+assert.sameValue(explicit, expected, "default precision is auto and no rounding");
+
+const propertyImplicit = time.toString({});
+assert.sameValue(propertyImplicit, expected, "default precision is auto and no rounding");
+
+const implicit = time.toString();
+assert.sameValue(implicit, expected, "default precision is auto and no rounding");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toString/prop-desc.js b/test/built-ins/Temporal/PlainTime/prototype/toString/prop-desc.js
new file mode 100644
index 00000000000..8c6639ce4fd
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toString/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-invalid-string.js b/test/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-invalid-string.js
new file mode 100644
index 00000000000..902833f63ce
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-invalid-string.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 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);
+assert.throws(RangeError, () => time.toString({ smallestUnit: "microsecond", roundingMode: "other string" }));
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-undefined.js b/test/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-undefined.js
new file mode 100644
index 00000000000..3a427ce6c60
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-wrong-type.js b/test/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-wrong-type.js
new file mode 100644
index 00000000000..805fe39ea2e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toString/roundingmode-wrong-type.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 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),
+);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-invalid-string.js b/test/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-invalid-string.js
new file mode 100644
index 00000000000..583b5696e9a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-invalid-string.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 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);
+assert.throws(RangeError, () => time.toString({ smallestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-plurals-accepted.js b/test/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-plurals-accepted.js
new file mode 100644
index 00000000000..9c3525fae2d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-plurals-accepted.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 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);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-undefined.js b/test/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-undefined.js
new file mode 100644
index 00000000000..a64bf38293f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-undefined.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-valid-units.js b/test/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-valid-units.js
new file mode 100644
index 00000000000..beb5fe4cb10
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-valid-units.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 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, 789, 999, 999);
+
+assert.sameValue(time.toString({ smallestUnit: "minute" }), "12:34");
+assert.sameValue(time.toString({ smallestUnit: "second" }), "12:34:56");
+assert.sameValue(time.toString({ smallestUnit: "millisecond" }), "12:34:56.789");
+assert.sameValue(time.toString({ smallestUnit: "microsecond" }), "12:34:56.789999");
+assert.sameValue(time.toString({ smallestUnit: "nanosecond" }), "12:34:56.789999999");
+
+const notValid = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+];
+
+notValid.forEach((smallestUnit) => {
+ assert.throws(RangeError, () => time.toString({ smallestUnit }), smallestUnit);
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-wrong-type.js b/test/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-wrong-type.js
new file mode 100644
index 00000000000..802bbf9af6a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-wrong-type.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 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),
+);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-plaindatetime.js b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-plaindatetime.js
new file mode 100644
index 00000000000..fafd121de4d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-plaindatetime.js
@@ -0,0 +1,29 @@
+// Copyright (C) 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");
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-primitive.js b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-primitive.js
new file mode 100644
index 00000000000..9cef2cdeff4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-primitive.js
@@ -0,0 +1,23 @@
+// Copyright (C) 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..09cba3a722e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 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" }));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..089b5b358dc
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 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_001, 86400_000_000_001].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" }));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..eb8ce9d5745
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 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" }));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/basic.js b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/basic.js
new file mode 100644
index 00000000000..2cbd2cb1673
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/basic.js
@@ -0,0 +1,24 @@
+// Copyright (C) 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('America/Los_Angeles');
+
+const objects = plainTime.toZonedDateTime({ timeZone, plainDate });
+assert.sameValue(objects.epochNanoseconds, 1594234800000000000n, "objects: epochNanoseconds");
+assert.sameValue(objects.timeZone, timeZone, "objects: timeZone");
+
+const timeZoneString = plainTime.toZonedDateTime({ timeZone: "America/Los_Angeles", plainDate });
+assert.sameValue(timeZoneString.epochNanoseconds, 1594234800000000000n, "timeZone string: epochNanoseconds");
+assert.sameValue(timeZoneString.timeZone.id, "America/Los_Angeles", "timeZone string: timeZone");
+
+const plainDateString = plainTime.toZonedDateTime({ timeZone, plainDate: "2020-07-08" });
+assert.sameValue(plainDateString.epochNanoseconds, 1594234800000000000n, "plainDate string: epochNanoseconds");
+assert.sameValue(plainDateString.timeZone.id, "America/Los_Angeles", "plainDate string: timeZone");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/builtin.js b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/builtin.js
new file mode 100644
index 00000000000..24d45e9e637
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-dateadd-called-with-options-undefined.js b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-dateadd-called-with-options-undefined.js
new file mode 100644
index 00000000000..6c10625d57d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-dateadd-called-with-options-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 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: >
+ BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the
+ options value
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddUndefinedOptions();
+const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9);
+const instance = new Temporal.PlainTime();
+instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(1970, 1, 1, calendar), timeZone });
+assert.sameValue(calendar.dateAddCallCount, 1);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-fields-iterable.js
new file mode 100644
index 00000000000..9bf110f06d6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-fields-iterable.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-temporal-object.js b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-temporal-object.js
new file mode 100644
index 00000000000..400e9cbacb2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/calendar-temporal-object.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 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.calendar, calendar, "Temporal object coerced to calendar");
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..7061b0fc39c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// 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");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/length.js b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/length.js
new file mode 100644
index 00000000000..62a7caaa07f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/name.js b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/name.js
new file mode 100644
index 00000000000..53fd0fe9f28
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/not-a-constructor.js b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/not-a-constructor.js
new file mode 100644
index 00000000000..279ec0b92e0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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)");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/plaindate-infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/plaindate-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..a17f45bbebb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/plaindate-infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// 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");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/prop-desc.js b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/prop-desc.js
new file mode 100644
index 00000000000..0cd061a5830
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..3ad1346c996
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 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);
+ assert.throws(RangeError, () => time.toZonedDateTime({ plainDate, timeZone }));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..3721ecbc3db
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 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_001, 86400_000_000_001].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);
+ assert.throws(RangeError, () => time.toZonedDateTime({ plainDate, timeZone }));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..2c45c48bb6c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 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);
+ assert.throws(TypeError, () => time.toZonedDateTime({ plainDate, timeZone }));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 00000000000..2fd76d4a056
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,42 @@
+// Copyright (C) 2021 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);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-datetime.js b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-datetime.js
new file mode 100644
index 00000000000..1a0b308000f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-datetime.js
@@ -0,0 +1,44 @@
+// Copyright (C) 2021 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");
+assert.throws(RangeError, () => instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone: { timeZone } }), "bare date-time string is not a time zone");
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+assert.sameValue(result1.timeZone.id, "UTC", "date-time + Z is UTC time zone");
+const result2 = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone: { timeZone } });
+assert.sameValue(result2.timeZone.id, "UTC", "date-time + Z is UTC time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result3 = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+assert.sameValue(result3.timeZone.id, "-07:00", "date-time + offset is the offset time zone");
+const result4 = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone: { timeZone } });
+assert.sameValue(result4.timeZone.id, "-07:00", "date-time + offset is the offset time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30[America/Vancouver]";
+const result5 = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+assert.sameValue(result5.timeZone.id, "America/Vancouver", "date-time + IANA annotation is the IANA time zone");
+const result6 = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone: { timeZone } });
+assert.sameValue(result6.timeZone.id, "America/Vancouver", "date-time + IANA annotation is the IANA time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30Z[America/Vancouver]";
+const result7 = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+assert.sameValue(result7.timeZone.id, "America/Vancouver", "date-time + Z + IANA annotation is the IANA time zone");
+const result8 = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone: { timeZone } });
+assert.sameValue(result8.timeZone.id, "America/Vancouver", "date-time + Z + IANA annotation is the IANA time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30-07:00[America/Vancouver]";
+const result9 = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
+assert.sameValue(result9.timeZone.id, "America/Vancouver", "date-time + offset + IANA annotation is the IANA time zone");
+const result10 = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone: { timeZone } });
+assert.sameValue(result10.timeZone.id, "America/Vancouver", "date-time + offset + IANA annotation is the IANA time zone (string in property bag)");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-balance-negative-time-units.js b/test/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 00000000000..35c808f2832
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 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 = new Temporal.TimeZone("-00:00:00.000000002");
+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);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-negative-epochnanoseconds.js b/test/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 00000000000..64031b23381
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 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 106.
+ 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);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..4c579f32554
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 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));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..c81871f722d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 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_001, 86400_000_000_001].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));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..5fb897fa7ea
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 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));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/balance-negative-time-units.js b/test/built-ins/Temporal/PlainTime/prototype/until/balance-negative-time-units.js
new file mode 100644
index 00000000000..7a30884b19d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/balance-negative-time-units.js
@@ -0,0 +1,48 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/builtin.js b/test/built-ins/Temporal/PlainTime/prototype/until/builtin.js
new file mode 100644
index 00000000000..669d05ea166
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/calendar-temporal-object.js b/test/built-ins/Temporal/PlainTime/prototype/until/calendar-temporal-object.js
new file mode 100644
index 00000000000..983c110723a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/calendar-temporal-object.js
@@ -0,0 +1,25 @@
+// Copyright (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.until
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaintime.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalTime(_other_).
+ sec-temporal-totemporaltime step 3.d:
+ d. If _calendar_ is not *undefined*, then
+ i. Set _calendar_ to ? ToTemporalCalendar(_calendar_).
+ ii. If ? ToString(_calendar_) is not *"iso8601"*, then
+ 1. Throw a *RangeError* exception.
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ assert.throws(RangeError, () => time.until({ hour: 12, minute: 30, calendar: temporalObject }));
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/largestunit-invalid-string.js b/test/built-ins/Temporal/PlainTime/prototype/until/largestunit-invalid-string.js
new file mode 100644
index 00000000000..2683390e906
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/largestunit-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 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);
+assert.throws(RangeError, () => earlier.until(later, { largestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/largestunit-plurals-accepted.js b/test/built-ins/Temporal/PlainTime/prototype/until/largestunit-plurals-accepted.js
new file mode 100644
index 00000000000..54c94fabe32
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/largestunit-plurals-accepted.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/largestunit-undefined.js b/test/built-ins/Temporal/PlainTime/prototype/until/largestunit-undefined.js
new file mode 100644
index 00000000000..d333e67d7a7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/largestunit-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/largestunit-wrong-type.js b/test/built-ins/Temporal/PlainTime/prototype/until/largestunit-wrong-type.js
new file mode 100644
index 00000000000..84aa559640a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/largestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 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),
+);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/length.js b/test/built-ins/Temporal/PlainTime/prototype/until/length.js
new file mode 100644
index 00000000000..0bae90deada
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/name.js b/test/built-ins/Temporal/PlainTime/prototype/until/name.js
new file mode 100644
index 00000000000..bc519f18438
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/not-a-constructor.js b/test/built-ins/Temporal/PlainTime/prototype/until/not-a-constructor.js
new file mode 100644
index 00000000000..4e30b6dfe90
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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)");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/options-undefined.js b/test/built-ins/Temporal/PlainTime/prototype/until/options-undefined.js
new file mode 100644
index 00000000000..c4b38867133
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/options-undefined.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 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, undefined);
+assert.sameValue(implicit.hours, 6, "default largest unit is hours");
+assert.sameValue(implicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/prop-desc.js b/test/built-ins/Temporal/PlainTime/prototype/until/prop-desc.js
new file mode 100644
index 00000000000..1b62794ee36
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-nan.js b/test/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-nan.js
new file mode 100644
index 00000000000..2dbdae0e16d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-nan.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 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 }));
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-non-integer.js b/test/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-non-integer.js
new file mode 100644
index 00000000000..f875e90a7f7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-non-integer.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 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: |
+ sec-temporal-totemporalroundingincrement step 7:
+ 7. Set _increment_ to floor(ℝ(_increment_)).
+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 });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, "roundingIncrement 2.5 floors to 2");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-out-of-range.js b/test/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-out-of-range.js
new file mode 100644
index 00000000000..064f18ed402
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-out-of-range.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 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: |
+ sec-temporal-totemporalroundingincrement step 6:
+ 6. If _increment_ < 1 or _increment_ > _maximum_, throw a *RangeError* exception.
+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: Infinity }));
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-undefined.js b/test/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-undefined.js
new file mode 100644
index 00000000000..4cea2367c05
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-wrong-type.js b/test/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-wrong-type.js
new file mode 100644
index 00000000000..216d77a53a7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/roundingincrement-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 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),
+);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/roundingmode-invalid-string.js b/test/built-ins/Temporal/PlainTime/prototype/until/roundingmode-invalid-string.js
new file mode 100644
index 00000000000..48cd2a79bcf
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/roundingmode-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 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);
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "microsecond", roundingMode: "other string" }));
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/roundingmode-undefined.js b/test/built-ins/Temporal/PlainTime/prototype/until/roundingmode-undefined.js
new file mode 100644
index 00000000000..ca0e24aba9c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/roundingmode-undefined.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 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 = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 123, 987, 500);
+
+const explicit1 = earlier.until(later, { smallestUnit: "microsecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 0, 1, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+const implicit1 = earlier.until(later, { smallestUnit: "microsecond" });
+TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 0, 1, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+
+const explicit2 = earlier.until(later, { smallestUnit: "millisecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 0, 1, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+const implicit2 = earlier.until(later, { smallestUnit: "millisecond" });
+TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 0, 1, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+
+const explicit3 = earlier.until(later, { smallestUnit: "second", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit3, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, "default roundingMode is trunc");
+const implicit3 = earlier.until(later, { smallestUnit: "second" });
+TemporalHelpers.assertDuration(implicit3, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, "default roundingMode is trunc");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/roundingmode-wrong-type.js b/test/built-ins/Temporal/PlainTime/prototype/until/roundingmode-wrong-type.js
new file mode 100644
index 00000000000..c8ba9d59f6b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/roundingmode-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 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),
+);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/smallestunit-invalid-string.js b/test/built-ins/Temporal/PlainTime/prototype/until/smallestunit-invalid-string.js
new file mode 100644
index 00000000000..8e759336538
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/smallestunit-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 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);
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/smallestunit-plurals-accepted.js b/test/built-ins/Temporal/PlainTime/prototype/until/smallestunit-plurals-accepted.js
new file mode 100644
index 00000000000..e7a0def265a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/smallestunit-plurals-accepted.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/smallestunit-undefined.js b/test/built-ins/Temporal/PlainTime/prototype/until/smallestunit-undefined.js
new file mode 100644
index 00000000000..e48c684e9a8
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/smallestunit-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/until/smallestunit-wrong-type.js b/test/built-ins/Temporal/PlainTime/prototype/until/smallestunit-wrong-type.js
new file mode 100644
index 00000000000..ebe6bf1705e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/until/smallestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 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),
+);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/valueOf/builtin.js b/test/built-ins/Temporal/PlainTime/prototype/valueOf/builtin.js
new file mode 100644
index 00000000000..643f92b1d65
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/valueOf/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/valueOf/length.js b/test/built-ins/Temporal/PlainTime/prototype/valueOf/length.js
new file mode 100644
index 00000000000..56351e36f32
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/valueOf/length.js
@@ -0,0 +1,25 @@
+// Copyright (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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/valueOf/name.js b/test/built-ins/Temporal/PlainTime/prototype/valueOf/name.js
new file mode 100644
index 00000000000..379ecc02d42
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/valueOf/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/valueOf/not-a-constructor.js b/test/built-ins/Temporal/PlainTime/prototype/valueOf/not-a-constructor.js
new file mode 100644
index 00000000000..6ac96440a04
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/valueOf/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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)");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/valueOf/prop-desc.js b/test/built-ins/Temporal/PlainTime/prototype/valueOf/prop-desc.js
new file mode 100644
index 00000000000..ab7a55c5229
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/valueOf/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/with/argument-not-object.js b/test/built-ins/Temporal/PlainTime/prototype/with/argument-not-object.js
new file mode 100644
index 00000000000..4a13aa54226
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/with/argument-not-object.js
@@ -0,0 +1,34 @@
+// Copyright (C) 2021 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);
+}
diff --git a/test/built-ins/Temporal/PlainTime/prototype/with/basic.js b/test/built-ins/Temporal/PlainTime/prototype/with/basic.js
new file mode 100644
index 00000000000..6333cc882f6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/with/basic.js
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/with/builtin.js b/test/built-ins/Temporal/PlainTime/prototype/with/builtin.js
new file mode 100644
index 00000000000..01059e6730c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/with/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/with/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainTime/prototype/with/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..8611613c2b1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/with/infinity-throws-rangeerror.js
@@ -0,0 +1,24 @@
+// 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");
+ });
+ });
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/with/length.js b/test/built-ins/Temporal/PlainTime/prototype/with/length.js
new file mode 100644
index 00000000000..05606553daf
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/with/length.js
@@ -0,0 +1,25 @@
+// Copyright (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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/with/name.js b/test/built-ins/Temporal/PlainTime/prototype/with/name.js
new file mode 100644
index 00000000000..f2cd06a7da4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/with/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/with/not-a-constructor.js b/test/built-ins/Temporal/PlainTime/prototype/with/not-a-constructor.js
new file mode 100644
index 00000000000..d26a2c657a1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/with/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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)");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/with/options-invalid.js b/test/built-ins/Temporal/PlainTime/prototype/with/options-invalid.js
new file mode 100644
index 00000000000..c0c64d4292d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/with/options-invalid.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2021 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));
+}
diff --git a/test/built-ins/Temporal/PlainTime/prototype/with/options-undefined.js b/test/built-ins/Temporal/PlainTime/prototype/with/options-undefined.js
new file mode 100644
index 00000000000..b79b6f916c2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/with/options-undefined.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/with/order-of-operations.js b/test/built-ins/Temporal/PlainTime/prototype/with/order-of-operations.js
new file mode 100644
index 00000000000..2571ba5cb87
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/with/order-of-operations.js
@@ -0,0 +1,60 @@
+// Copyright (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 = [
+ "get calendar",
+ "get timeZone",
+ "get hour",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get microsecond",
+ "get microsecond.valueOf",
+ "call microsecond.valueOf",
+ "get millisecond",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf",
+ "get minute",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get nanosecond",
+ "get nanosecond.valueOf",
+ "call nanosecond.valueOf",
+ "get second",
+ "get second.valueOf",
+ "call second.valueOf",
+];
+const actual = [];
+const fields = {
+ hour: 1.7,
+ minute: 1.7,
+ second: 1.7,
+ millisecond: 1.7,
+ microsecond: 1.7,
+ nanosecond: 1.7,
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = instance.with(argument);
+TemporalHelpers.assertPlainTime(result, 1, 1, 1, 1, 1, 1);
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/with/overflow-invalid-string.js b/test/built-ins/Temporal/PlainTime/prototype/with/overflow-invalid-string.js
new file mode 100644
index 00000000000..796cec5fcf3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/with/overflow-invalid-string.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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 values = ["", "CONSTRAIN", "balance", "other string"];
+for (const overflow of values) {
+ assert.throws(RangeError, () => time.with({ minute: 45 }, { overflow }));
+}
diff --git a/test/built-ins/Temporal/PlainTime/prototype/with/overflow-undefined.js b/test/built-ins/Temporal/PlainTime/prototype/with/overflow-undefined.js
new file mode 100644
index 00000000000..49e4ae8f293
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/with/overflow-undefined.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 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");
diff --git a/test/built-ins/Temporal/PlainTime/prototype/with/overflow-wrong-type.js b/test/built-ins/Temporal/PlainTime/prototype/with/overflow-wrong-type.js
new file mode 100644
index 00000000000..8cbc8742386
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/with/overflow-wrong-type.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 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),
+);
diff --git a/test/built-ins/Temporal/PlainTime/prototype/with/plaintimelike-invalid.js b/test/built-ins/Temporal/PlainTime/prototype/with/plaintimelike-invalid.js
new file mode 100644
index 00000000000..8e0bc2e31ae
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/with/plaintimelike-invalid.js
@@ -0,0 +1,47 @@
+// Copyright (C) 2021 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);
+}
diff --git a/test/built-ins/Temporal/PlainTime/prototype/with/prop-desc.js b/test/built-ins/Temporal/PlainTime/prototype/with/prop-desc.js
new file mode 100644
index 00000000000..f92c1787137
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/with/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 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,
+});
diff --git a/test/built-ins/Temporal/PlainTime/prototype/with/subclassing-ignored.js b/test/built-ins/Temporal/PlainTime/prototype/with/subclassing-ignored.js
new file mode 100644
index 00000000000..e06d11f76fc
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/prototype/with/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 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),
+);
diff --git a/test/built-ins/Temporal/PlainTime/second-undefined.js b/test/built-ins/Temporal/PlainTime/second-undefined.js
new file mode 100644
index 00000000000..b783a859f5d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainTime/second-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 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");
diff --git a/test/built-ins/Temporal/PlainYearMonth/builtin.js b/test/built-ins/Temporal/PlainYearMonth/builtin.js
new file mode 100644
index 00000000000..2ab0c93e87b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/builtin.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: Tests that Temporal.PlainYearMonth meets the requirements for built-in objects
+info: |
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.PlainYearMonth.prototype,
+ "object", "prototype property");
diff --git a/test/built-ins/Temporal/PlainYearMonth/calendar-invalid.js b/test/built-ins/Temporal/PlainYearMonth/calendar-invalid.js
new file mode 100644
index 00000000000..dd2fcc96f03
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/calendar-invalid.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainYearMonth throws a RangeError if the calendar argument is invalid
+esid: sec-temporal.plainyearmonth
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf"];
+const actual = [];
+const args = [
+ TemporalHelpers.toPrimitiveObserver(actual, 1970, "year"),
+ TemporalHelpers.toPrimitiveObserver(actual, 1, "month"),
+ "local",
+ TemporalHelpers.toPrimitiveObserver(actual, 1, "day")
+];
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(...args));
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainYearMonth/calendar-temporal-object.js b/test/built-ins/Temporal/PlainYearMonth/calendar-temporal-object.js
new file mode 100644
index 00000000000..e962ad9edb6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/calendar-temporal-object.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plainyearmonth step 5:
+ 5. Let _calendar_ be ? ToTemporalCalendarWithISODefault(_calendarLike_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const result = new Temporal.PlainYearMonth(2000, 5, temporalObject);
+ assert.sameValue(result.calendar, calendar, "Temporal object coerced to calendar");
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/calendar-undefined.js b/test/built-ins/Temporal/PlainYearMonth/calendar-undefined.js
new file mode 100644
index 00000000000..457e6324182
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/calendar-undefined.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: Calendar argument defaults to the built-in ISO 8601 calendar
+features: [Temporal]
+---*/
+
+const args = [2000, 5];
+
+Object.defineProperty(Temporal.Calendar, "from", {
+ get() {
+ throw new Test262Error("Should not get Calendar.from");
+ },
+});
+
+const dateExplicit = new Temporal.PlainYearMonth(...args, undefined);
+assert.sameValue(dateExplicit.calendar.toString(), "iso8601");
+
+const dateImplicit = new Temporal.PlainYearMonth(...args);
+assert.sameValue(dateImplicit.calendar.toString(), "iso8601");
diff --git a/test/built-ins/Temporal/PlainYearMonth/compare/builtin.js b/test/built-ins/Temporal/PlainYearMonth/compare/builtin.js
new file mode 100644
index 00000000000..49437b75955
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/compare/builtin.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: Tests that Temporal.PlainYearMonth.compare meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.compare),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.compare),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.compare),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.compare.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainYearMonth/compare/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainYearMonth/compare/calendar-fields-iterable.js
new file mode 100644
index 00000000000..d4776d70691
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/compare/calendar-fields-iterable.js
@@ -0,0 +1,37 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalYearMonth(_one_).
+ 2. Set _two_ to ? ToTemporalYearMonth(_two_).
+ sec-temporal-totemporalyearmonth step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+Temporal.PlainYearMonth.compare(
+ { year: 2000, month: 5, calendar: calendar1 },
+ { year: 2001, month: 6, calendar: calendar2 },
+);
+
+assert.sameValue(calendar1.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar1.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar1.iteratorExhausted[0], "iterated through the whole iterable");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainYearMonth/compare/calendar-temporal-object.js b/test/built-ins/Temporal/PlainYearMonth/compare/calendar-temporal-object.js
new file mode 100644
index 00000000000..5f5275f1a3d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/compare/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plainyearmonth.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalYearMonth(_one_).
+ 2. Set _two_ to ? ToTemporalYearMonth(_two_).
+ sec-temporal-totemporaldate step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ Temporal.PlainYearMonth.compare(
+ { year: 2000, month: 5, calendar: temporalObject },
+ { year: 2001, month: 6, calendar: temporalObject },
+ );
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/compare/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainYearMonth/compare/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..4d8c1ebc007
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/compare/infinity-throws-rangeerror.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in a property bag for either argument is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.compare
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const other = new Temporal.PlainYearMonth(2000, 5);
+const base = { year: 2000, month: 5 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month"].forEach((prop) => {
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare({ ...base, [prop]: inf }, other), `${prop} property cannot be ${inf}`);
+
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(other, { ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls1 = [];
+ const obj1 = TemporalHelpers.toPrimitiveObserver(calls1, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare({ ...base, [prop]: obj1 }, other));
+ assert.compareArray(calls1, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+
+ const calls2 = [];
+ const obj2 = TemporalHelpers.toPrimitiveObserver(calls2, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(other, { ...base, [prop]: obj2 }));
+ assert.compareArray(calls2, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/compare/length.js b/test/built-ins/Temporal/PlainYearMonth/compare/length.js
new file mode 100644
index 00000000000..f7b17076a1a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/compare/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: Temporal.PlainYearMonth.compare.length is 2
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.compare, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/compare/name.js b/test/built-ins/Temporal/PlainYearMonth/compare/name.js
new file mode 100644
index 00000000000..bdbef0824f4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/compare/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: Temporal.PlainYearMonth.compare.name is "compare".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.compare, "name", {
+ value: "compare",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/compare/not-a-constructor.js b/test/built-ins/Temporal/PlainYearMonth/compare/not-a-constructor.js
new file mode 100644
index 00000000000..3dac4d8d97a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/compare/not-a-constructor.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: Temporal.PlainYearMonth.compare does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.compare();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.compare), false,
+ "isConstructor(Temporal.PlainYearMonth.compare)");
diff --git a/test/built-ins/Temporal/PlainYearMonth/compare/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/compare/prop-desc.js
new file mode 100644
index 00000000000..fe4b6ef4ace
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/compare/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.compare
+description: The "compare" property of Temporal.PlainYearMonth
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.compare,
+ "function",
+ "`typeof PlainYearMonth.compare` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth, "compare", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/compare/use-internal-slots.js b/test/built-ins/Temporal/PlainYearMonth/compare/use-internal-slots.js
new file mode 100644
index 00000000000..5b2fde22b23
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/compare/use-internal-slots.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-sec-temporal.yearmonth.compare
+description: compare() ignores the observable properties and uses internal slots
+features: [Temporal]
+---*/
+
+function CustomError() {}
+
+class AvoidGettersYearMonth extends Temporal.PlainYearMonth {
+ get year() {
+ throw new CustomError();
+ }
+ get month() {
+ throw new CustomError();
+ }
+}
+
+const one = new AvoidGettersYearMonth(2000, 5);
+const two = new AvoidGettersYearMonth(2006, 3);
+assert.sameValue(Temporal.PlainYearMonth.compare(one, two), -1);
diff --git a/test/built-ins/Temporal/PlainYearMonth/constructor.js b/test/built-ins/Temporal/PlainYearMonth/constructor.js
new file mode 100644
index 00000000000..eee25f7bff5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/constructor.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: Temporal.PlainYearMonth constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.PlainYearMonth());
diff --git a/test/built-ins/Temporal/PlainYearMonth/from/argument-string.js b/test/built-ins/Temporal/PlainYearMonth/from/argument-string.js
new file mode 100644
index 00000000000..b3a48857260
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/from/argument-string.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const plainYearMonth = Temporal.PlainYearMonth.from("1970-12");
+TemporalHelpers.assertPlainYearMonth(plainYearMonth, 1970, 12, "M12");
+const fields = plainYearMonth.getISOFields();
+assert.sameValue(fields.calendar.id, "iso8601");
+assert.sameValue(fields.isoDay, 1, "isoDay");
+assert.sameValue(fields.isoMonth, 12, "isoMonth");
+assert.sameValue(fields.isoYear, 1970, "isoYear");
diff --git a/test/built-ins/Temporal/PlainYearMonth/from/builtin.js b/test/built-ins/Temporal/PlainYearMonth/from/builtin.js
new file mode 100644
index 00000000000..b586fb99225
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/from/builtin.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Tests that Temporal.PlainYearMonth.from meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.from.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainYearMonth/from/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainYearMonth/from/calendar-fields-iterable.js
new file mode 100644
index 00000000000..4676d5ab08b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/from/calendar-fields-iterable.js
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.from step 3:
+ 3. Return ? ToTemporalYearMonth(_item_, _options_).
+ sec-temporal-totemporalyearmonth step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+Temporal.PlainYearMonth.from({ year: 2000, month: 5, calendar });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainYearMonth/from/calendar-temporal-object.js b/test/built-ins/Temporal/PlainYearMonth/from/calendar-temporal-object.js
new file mode 100644
index 00000000000..f2d3897f465
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/from/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plainyearmonth.from step 3:
+ 3. Return ? ToTemporalYearMonth(_item_, _options_).
+ sec-temporal-totemporaldate step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const result = Temporal.PlainYearMonth.from({ year: 2000, month: 5, calendar: temporalObject });
+ assert.sameValue(result.calendar, calendar, "Temporal object coerced to calendar");
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/from/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainYearMonth/from/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..21a03bca67c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/from/infinity-throws-rangeerror.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.from
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const base = { year: 2000, month: 5 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ ...base, [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ ...base, [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/from/length.js b/test/built-ins/Temporal/PlainYearMonth/from/length.js
new file mode 100644
index 00000000000..40235dcb22b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/from/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Temporal.PlainYearMonth.from.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/from/name.js b/test/built-ins/Temporal/PlainYearMonth/from/name.js
new file mode 100644
index 00000000000..83973eecfe4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/from/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Temporal.PlainYearMonth.from.name is "from".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/from/not-a-constructor.js b/test/built-ins/Temporal/PlainYearMonth/from/not-a-constructor.js
new file mode 100644
index 00000000000..c48300ab8f0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/from/not-a-constructor.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Temporal.PlainYearMonth.from does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.from), false,
+ "isConstructor(Temporal.PlainYearMonth.from)");
diff --git a/test/built-ins/Temporal/PlainYearMonth/from/options-undefined.js b/test/built-ins/Temporal/PlainYearMonth/from/options-undefined.js
new file mode 100644
index 00000000000..88573c9c10f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/from/options-undefined.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const fields = { year: 2000, month: 13 };
+
+const explicit = Temporal.PlainYearMonth.from(fields, undefined);
+assert.sameValue(explicit.month, 12, "default overflow is constrain");
+
+const implicit = Temporal.PlainYearMonth.from(fields);
+assert.sameValue(implicit.month, 12, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainYearMonth/from/order-of-operations.js b/test/built-ins/Temporal/PlainYearMonth/from/order-of-operations.js
new file mode 100644
index 00000000000..f6f2ef142e1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/from/order-of-operations.js
@@ -0,0 +1,44 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Properties on an object passed to from() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get calendar",
+ "get month",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+];
+const actual = [];
+const fields = {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ if (key === "calendar") return Temporal.Calendar.from("iso8601");
+ const result = target[key];
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = Temporal.PlainYearMonth.from(argument);
+TemporalHelpers.assertPlainYearMonth(result, 1, 1, "M01");
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainYearMonth/from/overflow-invalid-string.js b/test/built-ins/Temporal/PlainYearMonth/from/overflow-invalid-string.js
new file mode 100644
index 00000000000..b0e4fb09316
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/from/overflow-invalid-string.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporalyearmonth steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ e. Return ? YearMonthFromFields(_calendar_, _fields_, _options_).
+ 3. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalYearMonth]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalYearMonth(_item_, _options_).
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainYearMonth(2000, 5),
+ { year: 2000, month: 5 },
+ "2000-05",
+];
+validValues.forEach((value) => {
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.from(value, { overflow: "other string" }));
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/from/overflow-undefined.js b/test/built-ins/Temporal/PlainYearMonth/from/overflow-undefined.js
new file mode 100644
index 00000000000..1d906e3c27a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/from/overflow-undefined.js
@@ -0,0 +1,41 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporalyearmonth steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ e. Return ? YearMonthFromFields(_calendar_, _fields_, _options_).
+ 3. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalYearMonth]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalYearMonth(_item_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainYearMonth(2000, 5),
+ "2000-05",
+];
+validValues.forEach((value) => {
+ const explicit = Temporal.PlainYearMonth.from(value, { overflow: undefined });
+ TemporalHelpers.assertPlainYearMonth(explicit, 2000, 5, "M05", "overflow is ignored");
+ const implicit = Temporal.PlainYearMonth.from(value, {});
+ TemporalHelpers.assertPlainYearMonth(implicit, 2000, 5, "M05", "overflow is ignored");
+});
+
+const propertyBag = { year: 2000, month: 13 };
+const explicit = Temporal.PlainYearMonth.from(propertyBag, { overflow: undefined });
+TemporalHelpers.assertPlainYearMonth(explicit, 2000, 12, "M12", "default overflow is constrain");
+const implicit = Temporal.PlainYearMonth.from(propertyBag, {});
+TemporalHelpers.assertPlainYearMonth(implicit, 2000, 12, "M12", "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainYearMonth/from/overflow-wrong-type.js b/test/built-ins/Temporal/PlainYearMonth/from/overflow-wrong-type.js
new file mode 100644
index 00000000000..5eacc955ff2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/from/overflow-wrong-type.js
@@ -0,0 +1,34 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-totemporalyearmonth steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ e. Return ? YearMonthFromFields(_calendar_, _fields_, _options_).
+ 3. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalYearMonth]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ b. Return ...
+ 3. Return ? ToTemporalYearMonth(_item_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.PlainYearMonth(2000, 5),
+ { year: 2000, month: 5 },
+ "2000-05",
+];
+validValues.forEach((value) => TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => Temporal.PlainYearMonth.from(value, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainYearMonth(result, 2000, 5, "M05", descr),
+));
diff --git a/test/built-ins/Temporal/PlainYearMonth/from/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/from/prop-desc.js
new file mode 100644
index 00000000000..7f4c318bb74
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/from/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: The "from" property of Temporal.PlainYearMonth
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.from,
+ "function",
+ "`typeof PlainYearMonth.from` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/from/subclassing-ignored.js b/test/built-ins/Temporal/PlainYearMonth/from/subclassing-ignored.js
new file mode 100644
index 00000000000..5e91cc04a56
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/from/subclassing-ignored.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.PlainYearMonth,
+ "from",
+ ["2000-05"],
+ (result) => TemporalHelpers.assertPlainYearMonth(result, 2000, 5, "M05"),
+);
diff --git a/test/built-ins/Temporal/PlainYearMonth/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainYearMonth/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..b81f09b70ef
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/infinity-throws-rangeerror.js
@@ -0,0 +1,41 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainYearMonth throws a RangeError if any numerical value is Infinity
+esid: sec-temporal.plainyearmonth
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const isoCalendar = Temporal.Calendar.from('iso8601');
+
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(Infinity, 1));
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(1970, Infinity));
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(1970, 1, isoCalendar, Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite year",
+ [O(Infinity, "year"), O(1, "month"), () => "iso8601", O(1, "day")],
+ ["get year.valueOf", "call year.valueOf"]
+ ],
+ [
+ "infinite month",
+ [O(1970, "year"), O(Infinity, "month"), () => "iso8601", O(1, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf"]
+ ],
+ [
+ "infinite day",
+ [O(1970, "year"), O(1, "month"), () => "iso8601", O(Infinity, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainYearMonth(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
diff --git a/test/built-ins/Temporal/PlainYearMonth/length.js b/test/built-ins/Temporal/PlainYearMonth/length.js
new file mode 100644
index 00000000000..b0874fc3eb4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: Temporal.PlainYearMonth.length is 2
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/missing-arguments.js b/test/built-ins/Temporal/PlainYearMonth/missing-arguments.js
new file mode 100644
index 00000000000..f19be5b5922
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/missing-arguments.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: RangeError thrown after processing given args when invoked without all required args
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "valueOf year",
+];
+const actual = [];
+const args = [
+ { valueOf() { actual.push("valueOf year"); return 1; } },
+];
+
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(...args));
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainYearMonth/name.js b/test/built-ins/Temporal/PlainYearMonth/name.js
new file mode 100644
index 00000000000..b999180eeb5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: Temporal.PlainYearMonth.name is "PlainYearMonth"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth, "name", {
+ value: "PlainYearMonth",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/negative-infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainYearMonth/negative-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..0f65524ae8c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,41 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainYearMonth throws a RangeError if any numerical value is -Infinity
+esid: sec-temporal.plainyearmonth
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const isoCalendar = Temporal.Calendar.from('iso8601');
+
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(-Infinity, 1));
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(1970, -Infinity));
+assert.throws(RangeError, () => new Temporal.PlainYearMonth(1970, 1, isoCalendar, -Infinity));
+
+const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName);
+const tests = [
+ [
+ "infinite year",
+ [O(-Infinity, "year"), O(1, "month"), () => "iso8601", O(1, "day")],
+ ["get year.valueOf", "call year.valueOf"]
+ ],
+ [
+ "infinite month",
+ [O(1970, "year"), O(-Infinity, "month"), () => "iso8601", O(1, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf"]
+ ],
+ [
+ "infinite day",
+ [O(1970, "year"), O(1, "month"), () => "iso8601", O(-Infinity, "day")],
+ ["get year.valueOf", "call year.valueOf", "get month.valueOf", "call month.valueOf", "get day.valueOf", "call day.valueOf"]
+ ],
+];
+
+for (const [description, args, expected] of tests) {
+ const actual = [];
+ const args_ = args.map((o) => o(actual));
+ assert.throws(RangeError, () => new Temporal.PlainYearMonth(...args_), description);
+ assert.compareArray(actual, expected, `${description} order of operations`);
+}
diff --git a/test/built-ins/Temporal/PlainYearMonth/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/prop-desc.js
new file mode 100644
index 00000000000..0ac93b2ebcf
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: The "PlainYearMonth" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth,
+ "function",
+ "`typeof PlainYearMonth` is `function`"
+);
+
+verifyProperty(Temporal, "PlainYearMonth", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/argument-not-object.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/argument-not-object.js
new file mode 100644
index 00000000000..38d7029fd60
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/argument-not-object.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Passing a primitive other than string to add() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 });
+assert.throws(RangeError, () => instance.add(undefined), "undefined");
+assert.throws(RangeError, () => instance.add(null), "null");
+assert.throws(RangeError, () => instance.add(true), "boolean");
+assert.throws(RangeError, () => instance.add(""), "empty string");
+assert.throws(TypeError, () => instance.add(Symbol()), "Symbol");
+assert.throws(RangeError, () => instance.add(7), "number");
+assert.throws(RangeError, () => instance.add(7n), "bigint");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string-negative-fractional-units.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string-negative-fractional-units.js
new file mode 100644
index 00000000000..92231565481
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string-negative-fractional-units.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+const resultHours = instance.add("-PT24.567890123H");
+TemporalHelpers.assertPlainYearMonth(resultHours, 2000, 5, "M05", "negative fractional hours");
+
+const resultMinutes = instance.add("-PT1440.567890123M");
+TemporalHelpers.assertPlainYearMonth(resultMinutes, 2000, 5, "M05", "negative fractional minutes");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string.js
new file mode 100644
index 00000000000..73c4fdc69bb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/argument-string.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 });
+const result = instance.add("P3M");
+TemporalHelpers.assertPlainYearMonth(result, 2000, 8, "M08");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/builtin.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/builtin.js
new file mode 100644
index 00000000000..5023a4b1e74
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.add
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.add),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.add),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.add),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.add.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-arguments-extra-options.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-arguments-extra-options.js
new file mode 100644
index 00000000000..dcb5c382b7e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-arguments-extra-options.js
@@ -0,0 +1,54 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: PlainYearMonth.prototype.add should pass extra fields in copied options objects.
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 5. Let yearMonth be ? Invoke(calendar, "yearMonthFromFields", « fields, options »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "get extra",
+ "get overflow",
+];
+const options = new Proxy({ extra: 5 }, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(date, duration, options) {
+ const result = super.dateAdd(date, duration, options);
+ options.overflow = 'meatloaf';
+ return result;
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.notSameValue(args[1], options, "args[1]");
+ return super.yearMonthFromFields(...args);
+ }
+}
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 3, new CustomCalendar());
+const result = plainYearMonth.add({ months: 5 }, options);
+TemporalHelpers.assertPlainYearMonth(result, 2000, 8, "M08");
+assert.compareArray(actual, expected, "extra field options object order of operations");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-arguments.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-arguments.js
new file mode 100644
index 00000000000..dd46462d751
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-arguments.js
@@ -0,0 +1,59 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: PlainYearMonth.prototype.add should respect calendar arguments and pass copied options objects.
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 5. Let yearMonth be ? Invoke(calendar, "yearMonthFromFields", « fields, options »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "get overflow",
+ "get overflow",
+ "get overflow.toString",
+ "call overflow.toString",
+ "get overflow.toString",
+ "call overflow.toString",
+];
+const options = new Proxy({ overflow: "constrain" }, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(date, duration, options) {
+ const result = super.dateAdd(date, duration, options);
+ options.overflow = 'meatloaf';
+ return result;
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.notSameValue(args[1], options, "args[1]");
+ return super.yearMonthFromFields(...args);
+ }
+}
+
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 3, new CustomCalendar());
+const result = plainYearMonth.add({ months: 10 }, options);
+TemporalHelpers.assertPlainYearMonth(result, 2001, 1, "M01");
+assert.compareArray(actual, expected, "copied options object order of operations");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-daysinmonth-wrong-value.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-daysinmonth-wrong-value.js
new file mode 100644
index 00000000000..d44cd9105be
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-daysinmonth-wrong-value.js
@@ -0,0 +1,42 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: >
+ The appropriate error is thrown if the calendar's daysInMonth method returns a
+ value that cannot be converted to a positive integer
+includes: [compareArray.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const actual = [];
+class CalendarDaysInMonthWrongValue extends Temporal.Calendar {
+ constructor(badValue) {
+ super("iso8601");
+ this._badValue = badValue;
+ }
+ dateFromFields(fields, options) {
+ actual.push("call dateFromFields");
+ return super.dateFromFields(fields, options);
+ }
+ daysInMonth() {
+ return this._badValue;
+ }
+}
+// daysInMonth is only called if we are adding a negative duration
+const duration = new Temporal.Duration(-1, -1);
+
+[Infinity, -Infinity, -42].forEach((badValue) => {
+ const calendar = new CalendarDaysInMonthWrongValue(badValue);
+ const yearMonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+ assert.throws(RangeError, () => yearMonth.add(duration), `daysInMonth ${badValue}`);
+ assert.compareArray(actual, [], "dateFromFields not called");
+});
+
+[Symbol('foo'), 31n].forEach((badValue) => {
+ const calendar = new CalendarDaysInMonthWrongValue(badValue);
+ const yearMonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+ assert.throws(TypeError, () => yearMonth.add(duration), `daysInMonth ${typeof badValue}`);
+ assert.compareArray(actual, [], "dateFromFields not called");
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-fields-iterable.js
new file mode 100644
index 00000000000..bc7a4053dac
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-fields-iterable.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.prototype.add step 8:
+ 8. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+yearmonth.add({ months: 1 });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..c7c90ab9865
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/infinity-throws-rangeerror.js
@@ -0,0 +1,35 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainYearMonth.prototype.add throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.plainyearmonth.prototype.add
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/length.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/length.js
new file mode 100644
index 00000000000..9923124f264
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Temporal.PlainYearMonth.prototype.add.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.add, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/name.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/name.js
new file mode 100644
index 00000000000..824454bdd2e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Temporal.PlainYearMonth.prototype.add.name is "add".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.add, "name", {
+ value: "add",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/negative-infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/negative-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..ce4bd1db75d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,35 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainYearMonth.prototype.add throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.plainyearmonth.prototype.add
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: -Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.add({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/non-integer-throws-rangeerror.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/non-integer-throws-rangeerror.js
new file mode 100644
index 00000000000..dfcd4a476bb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/non-integer-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.add({ [field]: -1.5 }));
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/not-a-constructor.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/not-a-constructor.js
new file mode 100644
index 00000000000..041790967bb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: >
+ Temporal.PlainYearMonth.prototype.add does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.add();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.add), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.add)");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/options-undefined.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/options-undefined.js
new file mode 100644
index 00000000000..c13d1ca8424
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/options-undefined.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+// overflow option has no effect on addition in the ISO calendar, so verify this
+// with a custom calendar
+class CheckedAdd extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(date, duration, options, constructor) {
+ this.called = true;
+ assert.notSameValue(options, undefined, "options not undefined");
+ return super.dateAdd(date, duration, options, constructor);
+ }
+}
+const calendar = new CheckedAdd();
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 1, calendar);
+const duration = { months: 1 };
+
+yearmonth.add(duration, undefined);
+yearmonth.add(duration);
+
+assert(calendar.called);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/order-of-operations.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/order-of-operations.js
new file mode 100644
index 00000000000..a4ee7787e05
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/order-of-operations.js
@@ -0,0 +1,74 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Properties on an object passed to add() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const expected = [
+ "get days",
+ "get days.valueOf",
+ "call days.valueOf",
+ "get hours",
+ "get hours.valueOf",
+ "call hours.valueOf",
+ "get microseconds",
+ "get microseconds.valueOf",
+ "call microseconds.valueOf",
+ "get milliseconds",
+ "get milliseconds.valueOf",
+ "call milliseconds.valueOf",
+ "get minutes",
+ "get minutes.valueOf",
+ "call minutes.valueOf",
+ "get months",
+ "get months.valueOf",
+ "call months.valueOf",
+ "get nanoseconds",
+ "get nanoseconds.valueOf",
+ "call nanoseconds.valueOf",
+ "get seconds",
+ "get seconds.valueOf",
+ "call seconds.valueOf",
+ "get weeks",
+ "get weeks.valueOf",
+ "call weeks.valueOf",
+ "get years",
+ "get years.valueOf",
+ "call years.valueOf",
+];
+const actual = [];
+const fields = {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = instance.add(argument);
+TemporalHelpers.assertPlainYearMonth(result, 2001, 6, "M06");
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-invalid-string.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-invalid-string.js
new file mode 100644
index 00000000000..03c9d816f6f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-invalid-string.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.add steps 13–15:
+ 13. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _date_, _durationToAdd_, _options_).
+ 14. ...
+ 15. Return ? YearMonthFromFields(_calendar_, _addedDateFields_, _options_).
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const duration = new Temporal.Duration(1, 1);
+assert.throws(RangeError, () => yearmonth.add(duration, { overflow: "other string" }));
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-undefined.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-undefined.js
new file mode 100644
index 00000000000..7f27befe44d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-undefined.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.add steps 13–15:
+ 13. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _date_, _durationToAdd_, _options_).
+ 14. ...
+ 15. Return ? YearMonthFromFields(_calendar_, _addedDateFields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// In the ISO calendar, PlainYearMonth.prototype.add() actually ignores the
+// overflow option. There is no addition in the ISO calendar that we could test
+// which would actually show a difference between the 'constrain' and 'reject'
+// values.
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const duration = new Temporal.Duration(1, 1);
+const explicit = yearmonth.add(duration, { overflow: undefined });
+TemporalHelpers.assertPlainYearMonth(explicit, 2001, 6, "M06", "default overflow is constrain");
+const implicit = yearmonth.add(duration, {});
+TemporalHelpers.assertPlainYearMonth(implicit, 2001, 6, "M06", "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-wrong-type.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-wrong-type.js
new file mode 100644
index 00000000000..30aba637d61
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/overflow-wrong-type.js
@@ -0,0 +1,47 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.add steps 13–15:
+ 13. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _date_, _durationToAdd_, _options_).
+ 14. ...
+ 15. Return ? YearMonthFromFields(_calendar_, _addedDateFields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const duration = new Temporal.Duration(1, 1);
+
+// See TemporalHelpers.checkStringOptionWrongType(); this code path has
+// different expectations for observable calls
+
+assert.throws(RangeError, () => yearmonth.add(duration, { overflow: null }), "null");
+assert.throws(RangeError, () => yearmonth.add(duration, { overflow: true }), "true");
+assert.throws(RangeError, () => yearmonth.add(duration, { overflow: false }), "false");
+assert.throws(TypeError, () => yearmonth.add(duration, { overflow: Symbol() }), "symbol");
+assert.throws(RangeError, () => yearmonth.add(duration, { overflow: 2n }), "bigint");
+assert.throws(RangeError, () => yearmonth.add(duration, { overflow: {} }), "plain object");
+
+// toString property is read once by Calendar.dateAdd() and then once again by
+// calendar.yearMonthFromFields().
+const expected = [
+ "get overflow.toString",
+ "call overflow.toString",
+ "get overflow.toString",
+ "call overflow.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");
+const result = yearmonth.add(duration, { overflow: observer });
+TemporalHelpers.assertPlainYearMonth(result, 2001, 6, "M06", "object with toString");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/prop-desc.js
new file mode 100644
index 00000000000..927ceb44b06
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: The "add" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.add,
+ "function",
+ "`typeof PlainYearMonth.prototype.add` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "add", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/subclassing-ignored.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/subclassing-ignored.js
new file mode 100644
index 00000000000..37f49b9a205
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.add
+description: Objects of a subclass are never created as return values for add()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainYearMonth,
+ [2000, 5],
+ "add",
+ [{ months: 1 }],
+ (result) => TemporalHelpers.assertPlainYearMonth(result, 2000, 6, "M06"),
+);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/calendar/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/prototype/calendar/prop-desc.js
new file mode 100644
index 00000000000..2a0eacf082e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/calendar/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.calendar
+description: The "calendar" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "calendar");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/prop-desc.js
new file mode 100644
index 00000000000..2e6a4dd8eb5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/daysInMonth/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.daysinmonth
+description: The "daysInMonth" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "daysInMonth");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/prop-desc.js
new file mode 100644
index 00000000000..a19da6738a8
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/daysInYear/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.daysinyear
+description: The "daysInYear" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "daysInYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-wrong-type.js b/test/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-wrong-type.js
new file mode 100644
index 00000000000..61bac736014
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-wrong-type.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: Appropriate error thrown when argument cannot be converted to a valid string
+features: [Symbol, Temporal]
+---*/
+
+const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5, day: 2 });
+
+assert.throws(RangeError, () => instance.equals(undefined), "undefined");
+assert.throws(RangeError, () => instance.equals(null), "null");
+assert.throws(RangeError, () => instance.equals(true), "true");
+assert.throws(RangeError, () => instance.equals(""), "empty string");
+assert.throws(TypeError, () => instance.equals(Symbol()), "symbol");
+assert.throws(RangeError, () => instance.equals(1), "1");
+assert.throws(TypeError, () => instance.equals({}), "plain object");
+assert.throws(TypeError, () => instance.equals(Temporal.PlainYearMonth), "Temporal.PlainYearMonth");
+assert.throws(TypeError, () => instance.equals(Temporal.PlainYearMonth.prototype), "Temporal.PlainYearMonth.prototype");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/equals/builtin.js b/test/built-ins/Temporal/PlainYearMonth/prototype/equals/builtin.js
new file mode 100644
index 00000000000..9f5913db4aa
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/equals/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.equals
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.equals),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.equals),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.equals),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.equals.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-fields-iterable.js
new file mode 100644
index 00000000000..90b1c8c53f9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-fields-iterable.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalYearMonth(_other_).
+ sec-temporal-totemporalyearmonth step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+yearmonth.equals({ year: 2005, month: 6, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-temporal-object.js b/test/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-temporal-object.js
new file mode 100644
index 00000000000..9af511590ed
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plainyearmonth.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalYearMonth(_other_).
+ sec-temporal-totemporalyearmonth step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const yearmonth = new Temporal.PlainYearMonth(2000, 5, temporalObject);
+ yearmonth.equals({ year: 2005, month: 6, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/equals/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainYearMonth/prototype/equals/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..e79f5734b9f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/equals/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.prototype.equals
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const base = { year: 2000, month: 5 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/equals/length.js b/test/built-ins/Temporal/PlainYearMonth/prototype/equals/length.js
new file mode 100644
index 00000000000..bc8be37b45c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/equals/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: Temporal.PlainYearMonth.prototype.equals.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.equals, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/equals/name.js b/test/built-ins/Temporal/PlainYearMonth/prototype/equals/name.js
new file mode 100644
index 00000000000..2f79e0d897c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/equals/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: Temporal.PlainYearMonth.prototype.equals.name is "equals".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.equals, "name", {
+ value: "equals",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/equals/not-a-constructor.js b/test/built-ins/Temporal/PlainYearMonth/prototype/equals/not-a-constructor.js
new file mode 100644
index 00000000000..ed3caf22be6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/equals/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: >
+ Temporal.PlainYearMonth.prototype.equals does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.equals();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.equals), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.equals)");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/equals/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/prototype/equals/prop-desc.js
new file mode 100644
index 00000000000..3c877eb4c62
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/equals/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.equals
+description: The "equals" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.equals,
+ "function",
+ "`typeof PlainYearMonth.prototype.equals` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "equals", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/builtin.js b/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/builtin.js
new file mode 100644
index 00000000000..190136a05cd
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getisofields
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.getISOFields
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.getISOFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.getISOFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.getISOFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.getISOFields.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-names.js b/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-names.js
new file mode 100644
index 00000000000..631c02891d6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-names.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getisofields
+description: Correct field names on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const ym = new Temporal.PlainYearMonth(2000, 5);
+
+const result = ym.getISOFields();
+assert.sameValue(result.isoYear, 2000, "isoYear result");
+assert.sameValue(result.isoMonth, 5, "isoMonth result");
+assert.sameValue(result.isoDay, 1, "isoDay result");
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-prop-desc.js
new file mode 100644
index 00000000000..260fb52982b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-prop-desc.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getisofields
+description: Properties on the returned object have the correct descriptor
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoMonth",
+ "isoYear",
+];
+
+const ym = new Temporal.PlainYearMonth(2000, 5);
+const result = ym.getISOFields();
+
+for (const property of expected) {
+ verifyProperty(result, property, {
+ writable: true,
+ enumerable: true,
+ configurable: true,
+ });
+}
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-traversal-order.js b/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-traversal-order.js
new file mode 100644
index 00000000000..e2b1099f1b7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/field-traversal-order.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getisofields
+description: Properties added in correct order to object returned from getISOFields
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoMonth",
+ "isoYear",
+];
+
+const ym = new Temporal.PlainYearMonth(2000, 5);
+const result = ym.getISOFields();
+
+assert.compareArray(Object.keys(result), expected);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/length.js b/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/length.js
new file mode 100644
index 00000000000..4037863e6e7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getisofields
+description: Temporal.PlainYearMonth.prototype.getISOFields.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.getISOFields, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/name.js b/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/name.js
new file mode 100644
index 00000000000..5b38b8809a0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getisofields
+description: Temporal.PlainYearMonth.prototype.getISOFields.name is "getISOFields".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.getISOFields, "name", {
+ value: "getISOFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/not-a-constructor.js b/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/not-a-constructor.js
new file mode 100644
index 00000000000..ee99732e401
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getisofields
+description: >
+ Temporal.PlainYearMonth.prototype.getISOFields does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.getISOFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.getISOFields), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.getISOFields)");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/prop-desc.js
new file mode 100644
index 00000000000..ae6d5240779
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/getISOFields/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.getisofields
+description: The "getISOFields" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.getISOFields,
+ "function",
+ "`typeof PlainYearMonth.prototype.getISOFields` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "getISOFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/prop-desc.js
new file mode 100644
index 00000000000..bd185e3456d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/inLeapYear/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.inleapyear
+description: The "inLeapYear" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "inLeapYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/month/calendar-returns-infinity.js b/test/built-ins/Temporal/PlainYearMonth/prototype/month/calendar-returns-infinity.js
new file mode 100644
index 00000000000..54919c7ba4d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/month/calendar-returns-infinity.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.month
+description: Getter throws if the calendar returns ±∞ from its month method
+features: [Temporal]
+---*/
+
+class InfinityCalendar extends Temporal.Calendar {
+ constructor(positive) {
+ super("iso8601");
+ this.positive = positive;
+ }
+
+ month() {
+ return this.positive ? Infinity : -Infinity;
+ }
+}
+
+const pos = new InfinityCalendar(true);
+const instance1 = new Temporal.PlainYearMonth(2000, 5, pos);
+assert.throws(RangeError, () => instance1.month);
+
+const neg = new InfinityCalendar(false);
+const instance2 = new Temporal.PlainYearMonth(2000, 5, neg);
+assert.throws(RangeError, () => instance2.month);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/month/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/prototype/month/prop-desc.js
new file mode 100644
index 00000000000..263ea6fa016
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/month/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.month
+description: The "month" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "month");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/monthCode/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/prototype/monthCode/prop-desc.js
new file mode 100644
index 00000000000..d9f41958d7a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/monthCode/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.monthcode
+description: The "monthCode" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "monthCode");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/prop-desc.js
new file mode 100644
index 00000000000..889a7303366
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/monthsInYear/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.monthsinyear
+description: The "monthsInYear" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "monthsInYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/builtin.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/builtin.js
new file mode 100644
index 00000000000..a22ac266f1e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.since
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.since),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.since),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.since),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.since.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 00000000000..e4bf11cd734
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.plainyearmonth.prototype.since steps 21–22:
+ 21. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _largestUnit_).
+ 22. Let _result_ be ? CalendarDateUntil(_calendar_, _thisDate_, _otherDate_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.PlainYearMonth(2000, 5, calendar);
+ const later = new Temporal.PlainYearMonth(2001, 6, calendar);
+ later.since(earlier, { largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"]
+ }
+);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-fields-iterable.js
new file mode 100644
index 00000000000..c7b4938043f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-fields-iterable.js
@@ -0,0 +1,40 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalYearMonth(_other_).
+ sec-temporal.plainyearmonth.prototype.since step 14:
+ 14. Let fieldNames be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal-totemporalyearmonth step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "monthCode",
+ "year",
+];
+const expected2 = [
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+yearmonth.since({ year: 2005, month: 6, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 1, "fields() method not called");
+assert.compareArray(calendar1.fieldsCalledWith[0], expected1, "fields() method called with correct args");
+assert(calendar1.iteratorExhausted[0], "iterated through the whole iterable");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected2, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-temporal-object.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-temporal-object.js
new file mode 100644
index 00000000000..b4804389c9e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plainyearmonth.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalYearMonth(_other_).
+ sec-temporal-totemporalyearmonth step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const yearmonth = new Temporal.PlainYearMonth(2000, 5, temporalObject);
+ yearmonth.since({ year: 2005, month: 6, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..8ef239002bc
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.prototype.since
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const base = { year: 2000, month: 5 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.since({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.since({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-invalid-string.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-invalid-string.js
new file mode 100644
index 00000000000..cabf7bd14fd
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+assert.throws(RangeError, () => later.since(earlier, { largestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-plurals-accepted.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-plurals-accepted.js
new file mode 100644
index 00000000000..791d44106e1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-plurals-accepted.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const validUnits = [
+ "year",
+ "month",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => later.since(earlier, { largestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-undefined.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-undefined.js
new file mode 100644
index 00000000000..a46007e1619
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+const explicit = later.since(earlier, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-wrong-type.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-wrong-type.js
new file mode 100644
index 00000000000..98b20c831e0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/largestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "month",
+ (largestUnit) => later.since(earlier, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/length.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/length.js
new file mode 100644
index 00000000000..243aef2ede5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Temporal.PlainYearMonth.prototype.since.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.since, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/name.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/name.js
new file mode 100644
index 00000000000..4cfda0d01b9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Temporal.PlainYearMonth.prototype.since.name is "since".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.since, "name", {
+ value: "since",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/not-a-constructor.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/not-a-constructor.js
new file mode 100644
index 00000000000..090e056fe8a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: >
+ Temporal.PlainYearMonth.prototype.since does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.since();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.since), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.since)");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/options-undefined.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/options-undefined.js
new file mode 100644
index 00000000000..c55628a010b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/options-undefined.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2002, 12);
+
+const explicit = later.since(earlier, undefined);
+assert.sameValue(explicit.years, 2, "default largest unit is years");
+assert.sameValue(explicit.months, 7, "default smallest unit is months and rounding is none");
+
+const implicit = later.since(earlier);
+assert.sameValue(implicit.years, 2, "default largest unit is years");
+assert.sameValue(implicit.months, 7, "default smallest unit is months and rounding is none");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/prop-desc.js
new file mode 100644
index 00000000000..d6ed3d6bdee
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: The "since" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.since,
+ "function",
+ "`typeof PlainYearMonth.prototype.since` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "since", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-nan.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-nan.js
new file mode 100644
index 00000000000..945235c9094
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-nan.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plainyearmonth.prototype.since step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: NaN }));
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-non-integer.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-non-integer.js
new file mode 100644
index 00000000000..0b9cc88ef38
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-non-integer.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Rounding for roundingIncrement option
+info: |
+ sec-temporal-totemporalroundingincrement step 7:
+ 7. Set _increment_ to floor(ℝ(_increment_)).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2000, 10);
+const result = later.since(earlier, { roundingIncrement: 2.5 });
+TemporalHelpers.assertDuration(result, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, "roundingIncrement 2.5 floors to 2");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-out-of-range.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-out-of-range.js
new file mode 100644
index 00000000000..50710ab183d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-out-of-range.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ sec-temporal-totemporalroundingincrement step 6:
+ 6. If _increment_ < 1 or _increment_ > _maximum_, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2000, 10);
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: Infinity }));
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-undefined.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-undefined.js
new file mode 100644
index 00000000000..a5ed492f617
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plainyearmonth.prototype.since step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+const explicit = later.since(earlier, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingIncrement is 1");
+
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingIncrement is 1");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-wrong-type.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-wrong-type.js
new file mode 100644
index 00000000000..6aec0fdc7d9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingincrement-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plainyearmonth.prototype.since step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => later.since(earlier, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-invalid-string.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-invalid-string.js
new file mode 100644
index 00000000000..14cbe1ac2fb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "microsecond", roundingMode: "other string" }));
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-undefined.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-undefined.js
new file mode 100644
index 00000000000..889c5050777
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-undefined.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 1);
+
+const later1 = new Temporal.PlainYearMonth(2005, 2);
+const explicit1 = later1.since(earlier, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit1 = later1.since(earlier, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+
+const later2 = new Temporal.PlainYearMonth(2005, 12);
+const explicit2 = later2.since(earlier, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit2 = later2.since(earlier, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-wrong-type.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-wrong-type.js
new file mode 100644
index 00000000000..f363ea1e16f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/roundingmode-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => later.since(earlier, { smallestUnit: "year", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-invalid-string.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-invalid-string.js
new file mode 100644
index 00000000000..64e17adf844
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-plurals-accepted.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-plurals-accepted.js
new file mode 100644
index 00000000000..34ac2acb240
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-plurals-accepted.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const validUnits = [
+ "year",
+ "month",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => later.since(earlier, { smallestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-undefined.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-undefined.js
new file mode 100644
index 00000000000..5e84ad20be1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+const explicit = later.since(earlier, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default smallestUnit is month");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default smallestUnit is month");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-wrong-type.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-wrong-type.js
new file mode 100644
index 00000000000..6e9429a17d9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/smallestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.since
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "year",
+ (smallestUnit) => later.since(earlier, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-not-object.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-not-object.js
new file mode 100644
index 00000000000..d637cf0e53e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-not-object.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Passing a primitive other than string to subtract() throws
+features: [Symbol, Temporal]
+---*/
+
+const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 });
+assert.throws(RangeError, () => instance.subtract(undefined), "undefined");
+assert.throws(RangeError, () => instance.subtract(null), "null");
+assert.throws(RangeError, () => instance.subtract(true), "boolean");
+assert.throws(RangeError, () => instance.subtract(""), "empty string");
+assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol");
+assert.throws(RangeError, () => instance.subtract(7), "number");
+assert.throws(RangeError, () => instance.subtract(7n), "bigint");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string-negative-fractional-units.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string-negative-fractional-units.js
new file mode 100644
index 00000000000..061060e979a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string-negative-fractional-units.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Strings with fractional duration units are treated with the correct sign
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+const resultHours = instance.subtract("-PT24.567890123H");
+TemporalHelpers.assertPlainYearMonth(resultHours, 2000, 5, "M05", "negative fractional hours");
+
+const resultMinutes = instance.subtract("-PT1440.567890123M");
+TemporalHelpers.assertPlainYearMonth(resultMinutes, 2000, 5, "M05", "negative fractional minutes");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string.js
new file mode 100644
index 00000000000..5de225dca68
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: A string is parsed into the correct object when passed as the argument
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 });
+const result = instance.subtract("P3M");
+TemporalHelpers.assertPlainYearMonth(result, 2000, 2, "M02");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin.js
new file mode 100644
index 00000000000..f1f880fd90e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.subtract
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.subtract),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.subtract),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.subtract),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.subtract.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments-extra-options.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments-extra-options.js
new file mode 100644
index 00000000000..085c3fe83c2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments-extra-options.js
@@ -0,0 +1,54 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: plainyearmonth.prototype.subtract should pass extra fields in copied options objects.
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 5. Let yearMonth be ? Invoke(calendar, "yearMonthFromFields", « fields, options »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "get extra",
+ "get overflow",
+];
+const options = new Proxy({ extra: 5 }, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(date, duration, options) {
+ const result = super.dateAdd(date, duration, options);
+ options.overflow = 'meatloaf';
+ return result;
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.notSameValue(args[1], options, "args[1]");
+ return super.yearMonthFromFields(...args);
+ }
+}
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 3, new CustomCalendar());
+const result = plainYearMonth.subtract({ months: 5 }, options);
+TemporalHelpers.assertPlainYearMonth(result, 1999, 10, "M10");
+assert.compareArray(actual, expected, "extra field options object order of operations");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments.js
new file mode 100644
index 00000000000..008df86c6b5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments.js
@@ -0,0 +1,58 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: plainyearmonth.prototype.subtract should respect calendar arguments and pass copied options objects.
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 5. Let yearMonth be ? Invoke(calendar, "yearMonthFromFields", « fields, options »).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "get overflow",
+ "get overflow",
+ "get overflow.toString",
+ "call overflow.toString",
+ "get overflow.toString",
+ "call overflow.toString",
+];
+const options = new Proxy({ overflow: "constrain" }, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(date, duration, options) {
+ const result = super.dateAdd(date, duration, options);
+ options.overflow = 'meatloaf';
+ return result;
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.notSameValue(args[1], options, "args[1]");
+ return super.yearMonthFromFields(...args);
+ }
+}
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 7, new CustomCalendar());
+const result = plainYearMonth.subtract({ months: 9 }, options);
+TemporalHelpers.assertPlainYearMonth(result, 1999, 10, "M10");
+assert.compareArray(actual, expected, "copied options object order of operations");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-daysinmonth-wrong-value.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-daysinmonth-wrong-value.js
new file mode 100644
index 00000000000..50d7feeab7a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-daysinmonth-wrong-value.js
@@ -0,0 +1,42 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: >
+ The appropriate error is thrown if the calendar's daysInMonth method returns a
+ value that cannot be converted to a positive integer
+includes: [compareArray.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const actual = [];
+class CalendarDaysInMonthWrongValue extends Temporal.Calendar {
+ constructor(badValue) {
+ super("iso8601");
+ this._badValue = badValue;
+ }
+ dateFromFields(fields, options) {
+ actual.push("call dateFromFields");
+ return super.dateFromFields(fields, options);
+ }
+ daysInMonth() {
+ return this._badValue;
+ }
+}
+// daysInMonth is only called if we are subtracting a positive duration
+const duration = new Temporal.Duration(1, 1);
+
+[Infinity, -Infinity, -42].forEach((badValue) => {
+ const calendar = new CalendarDaysInMonthWrongValue(badValue);
+ const yearMonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+ assert.throws(RangeError, () => yearMonth.subtract(duration), `daysInMonth ${badValue}`);
+ assert.compareArray(actual, [], "dateFromFields not called");
+});
+
+[Symbol('foo'), 31n].forEach((badValue) => {
+ const calendar = new CalendarDaysInMonthWrongValue(badValue);
+ const yearMonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+ assert.throws(TypeError, () => yearMonth.subtract(duration), `daysInMonth ${typeof badValue}`);
+ assert.compareArray(actual, [], "dateFromFields not called");
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fields-iterable.js
new file mode 100644
index 00000000000..8329c5b6f58
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fields-iterable.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.prototype.subtract step 8:
+ 8. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+yearmonth.subtract({ months: 1 });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..4801489331b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/infinity-throws-rangeerror.js
@@ -0,0 +1,34 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainYearMonth.prototype.subtract throws a RangeError if any value in a property bag is Infinity
+esid: sec-temporal.plainyearmonth.prototype.subtract
+features: [Temporal]
+---*/
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/length.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/length.js
new file mode 100644
index 00000000000..5c17ed84511
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Temporal.PlainYearMonth.prototype.subtract.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.subtract, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/name.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/name.js
new file mode 100644
index 00000000000..347e1a44573
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Temporal.PlainYearMonth.prototype.subtract.name is "subtract".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.subtract, "name", {
+ value: "subtract",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/negative-infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/negative-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..2079930fda9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/negative-infinity-throws-rangeerror.js
@@ -0,0 +1,35 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Temporal.PlainYearMonth.prototype.subtract throws a RangeError if any value in a property bag is -Infinity
+esid: sec-temporal.plainyearmonth.prototype.subtract
+features: [Temporal]
+---*/
+
+const overflows = ["constrain", "reject"];
+const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+
+const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 });
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: -Infinity }, { overflow }));
+ });
+});
+
+let calls = 0;
+const obj = {
+ valueOf() {
+ calls++;
+ return -Infinity;
+ }
+};
+
+overflows.forEach((overflow) => {
+ fields.forEach((field) => {
+ calls = 0;
+ assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow }));
+ assert.sameValue(calls, 1, "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/non-integer-throws-rangeerror.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/non-integer-throws-rangeerror.js
new file mode 100644
index 00000000000..252bb15ae81
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/non-integer-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.subtract({ [field]: -1.5 }));
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/not-a-constructor.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/not-a-constructor.js
new file mode 100644
index 00000000000..18ce8eb5bb2
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: >
+ Temporal.PlainYearMonth.prototype.subtract does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.subtract();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.subtract), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.subtract)");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-undefined.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-undefined.js
new file mode 100644
index 00000000000..3588779fe8b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-undefined.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+// overflow option has no effect on addition in the ISO calendar, so verify this
+// with a custom calendar
+class CheckedAdd extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(date, duration, options, constructor) {
+ this.called = true;
+ assert.notSameValue(options, undefined, "options not undefined");
+ return super.dateAdd(date, duration, options, constructor);
+ }
+}
+const calendar = new CheckedAdd();
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 3, calendar);
+const duration = { months: 1 };
+
+yearmonth.subtract(duration, undefined);
+yearmonth.subtract(duration);
+
+assert(calendar.called);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js
new file mode 100644
index 00000000000..1180777506d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js
@@ -0,0 +1,74 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Properties on an object passed to subtract() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const expected = [
+ "get days",
+ "get days.valueOf",
+ "call days.valueOf",
+ "get hours",
+ "get hours.valueOf",
+ "call hours.valueOf",
+ "get microseconds",
+ "get microseconds.valueOf",
+ "call microseconds.valueOf",
+ "get milliseconds",
+ "get milliseconds.valueOf",
+ "call milliseconds.valueOf",
+ "get minutes",
+ "get minutes.valueOf",
+ "call minutes.valueOf",
+ "get months",
+ "get months.valueOf",
+ "call months.valueOf",
+ "get nanoseconds",
+ "get nanoseconds.valueOf",
+ "call nanoseconds.valueOf",
+ "get seconds",
+ "get seconds.valueOf",
+ "call seconds.valueOf",
+ "get weeks",
+ "get weeks.valueOf",
+ "call weeks.valueOf",
+ "get years",
+ "get years.valueOf",
+ "call years.valueOf",
+];
+const actual = [];
+const fields = {
+ years: 1,
+ months: 1,
+ weeks: 1,
+ days: 1,
+ hours: 1,
+ minutes: 1,
+ seconds: 1,
+ milliseconds: 1,
+ microseconds: 1,
+ nanoseconds: 1,
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = instance.subtract(argument);
+TemporalHelpers.assertPlainYearMonth(result, 1999, 4, "M04");
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-invalid-string.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-invalid-string.js
new file mode 100644
index 00000000000..aa3ed01cf6a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-invalid-string.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.subtract steps 13–15:
+ 13. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _date_, _durationToAdd_, _options_).
+ 14. ...
+ 15. Return ? YearMonthFromFields(_calendar_, _addedDateFields_, _options_).
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const duration = new Temporal.Duration(1, 1);
+assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: "other string" }));
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-undefined.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-undefined.js
new file mode 100644
index 00000000000..0837beac76d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-undefined.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.subtract steps 13–15:
+ 13. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _date_, _durationToAdd_, _options_).
+ 14. ...
+ 15. Return ? YearMonthFromFields(_calendar_, _addedDateFields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// In the ISO calendar, PlainYearMonth.prototype.subtract() actually ignores the
+// overflow option. There is no subtraction in the ISO calendar that we could
+// test which would actually show a difference between the 'constrain' and
+// 'reject' values.
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const duration = new Temporal.Duration(1, 1);
+const explicit = yearmonth.subtract(duration, { overflow: undefined });
+TemporalHelpers.assertPlainYearMonth(explicit, 1999, 4, "M04", "default overflow is constrain");
+const implicit = yearmonth.subtract(duration, {});
+TemporalHelpers.assertPlainYearMonth(implicit, 1999, 4, "M04", "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-wrong-type.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-wrong-type.js
new file mode 100644
index 00000000000..4174c2ccfd3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-wrong-type.js
@@ -0,0 +1,47 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.subtract steps 13–15:
+ 13. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _date_, _durationToAdd_, _options_).
+ 14. ...
+ 15. Return ? YearMonthFromFields(_calendar_, _addedDateFields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const duration = new Temporal.Duration(1, 1);
+
+// See TemporalHelpers.checkStringOptionWrongType(); this code path has
+// different expectations for observable calls
+
+assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: null }), "null");
+assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: true }), "true");
+assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: false }), "false");
+assert.throws(TypeError, () => yearmonth.subtract(duration, { overflow: Symbol() }), "symbol");
+assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: 2n }), "bigint");
+assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: {} }), "plain object");
+
+// toString property is read once by Calendar.dateAdd() and then once again by
+// calendar.yearMonthFromFields().
+const expected = [
+ "get overflow.toString",
+ "call overflow.toString",
+ "get overflow.toString",
+ "call overflow.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");
+const result = yearmonth.subtract(duration, { overflow: observer });
+TemporalHelpers.assertPlainYearMonth(result, 1999, 4, "M04", "object with toString");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/prop-desc.js
new file mode 100644
index 00000000000..981f1091162
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: The "subtract" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.subtract,
+ "function",
+ "`typeof PlainYearMonth.prototype.subtract` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "subtract", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/subclassing-ignored.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/subclassing-ignored.js
new file mode 100644
index 00000000000..40a021baafc
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.subtract
+description: Objects of a subclass are never created as return values for subtract()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainYearMonth,
+ [2000, 5],
+ "subtract",
+ [{ months: 1 }],
+ (result) => TemporalHelpers.assertPlainYearMonth(result, 2000, 4, "M04"),
+);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toJSON/builtin.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toJSON/builtin.js
new file mode 100644
index 00000000000..0c1274aa37b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toJSON/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tojson
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.toJSON
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toJSON/length.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toJSON/length.js
new file mode 100644
index 00000000000..6f7808fa201
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toJSON/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tojson
+description: Temporal.PlainYearMonth.prototype.toJSON.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toJSON/name.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toJSON/name.js
new file mode 100644
index 00000000000..e9c9eb564b1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toJSON/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tojson
+description: Temporal.PlainYearMonth.prototype.toJSON.name is "toJSON".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toJSON/not-a-constructor.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 00000000000..0a1bcfb757b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toJSON/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tojson
+description: >
+ Temporal.PlainYearMonth.prototype.toJSON does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.toJSON), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.toJSON)");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toJSON/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toJSON/prop-desc.js
new file mode 100644
index 00000000000..bf1635153fc
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toJSON/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tojson
+description: The "toJSON" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.toJSON,
+ "function",
+ "`typeof PlainYearMonth.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/builtin.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/builtin.js
new file mode 100644
index 00000000000..d29dd0831bb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tolocalestring
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.toLocaleString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.toLocaleString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.toLocaleString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.toLocaleString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.toLocaleString.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/length.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/length.js
new file mode 100644
index 00000000000..6af3a1443b6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tolocalestring
+description: Temporal.PlainYearMonth.prototype.toLocaleString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.toLocaleString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/locales-undefined.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/locales-undefined.js
new file mode 100644
index 00000000000..c0a6f4750b0
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/locales-undefined.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tolocalestring
+description: Omitting the locales argument defaults to the DateTimeFormat default
+features: [Temporal]
+---*/
+
+const defaultFormatter = new Intl.DateTimeFormat([], Object.create(null));
+const { calendar } = defaultFormatter.resolvedOptions();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+const expected = defaultFormatter.format(yearmonth);
+
+const actualExplicit = yearmonth.toLocaleString(undefined);
+assert.sameValue(actualExplicit, expected, "default locale is determined by Intl.DateTimeFormat");
+
+const actualImplicit = yearmonth.toLocaleString();
+assert.sameValue(actualImplicit, expected, "default locale is determined by Intl.DateTimeFormat");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/name.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/name.js
new file mode 100644
index 00000000000..0be23c10ebc
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tolocalestring
+description: Temporal.PlainYearMonth.prototype.toLocaleString.name is "toLocaleString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.toLocaleString, "name", {
+ value: "toLocaleString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/not-a-constructor.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/not-a-constructor.js
new file mode 100644
index 00000000000..859a4d02d10
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tolocalestring
+description: >
+ Temporal.PlainYearMonth.prototype.toLocaleString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.toLocaleString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.toLocaleString), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.toLocaleString)");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/options-undefined.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/options-undefined.js
new file mode 100644
index 00000000000..5e3f04b00e3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/options-undefined.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tolocalestring
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const defaultFormatter = new Intl.DateTimeFormat('en', Object.create(null));
+const { calendar } = defaultFormatter.resolvedOptions();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+const expected = defaultFormatter.format(yearmonth);
+
+const actualExplicit = yearmonth.toLocaleString('en', undefined);
+assert.sameValue(actualExplicit, expected, "default locale is determined by Intl.DateTimeFormat");
+
+const actualImplicit = yearmonth.toLocaleString('en');
+assert.sameValue(actualImplicit, expected, "default locale is determined by Intl.DateTimeFormat");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/prop-desc.js
new file mode 100644
index 00000000000..f65d5846382
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toLocaleString/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tolocalestring
+description: The "toLocaleString" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.toLocaleString,
+ "function",
+ "`typeof PlainYearMonth.prototype.toLocaleString` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "toLocaleString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/argument-not-object.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/argument-not-object.js
new file mode 100644
index 00000000000..16c1e063404
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/argument-not-object.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: Throws a TypeError if the argument is not an Object, before any other observable actions
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[null, undefined, true, 3.1416, "a string", Symbol("symbol"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarThrowEverything();
+ const plainYearMonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+ assert.throws(TypeError, () => plainYearMonth.toPlainDate(primitive));
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin.js
new file mode 100644
index 00000000000..8473ff7c2f6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.toPlainDate
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.toPlainDate),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.toPlainDate),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.toPlainDate),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.toPlainDate.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-fields-iterable.js
new file mode 100644
index 00000000000..7d990bf231c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-fields-iterable.js
@@ -0,0 +1,34 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.prototype.toplaindate step 5:
+ 5. Let _receiverFieldNames_ be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal.plainyearmonth.prototype.toplaindate step 7:
+ 7. Let _inputFieldNames_ be ? CalendarFields(_calendar_, « *"day"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "monthCode",
+ "year",
+];
+const expected2 = [
+ "day",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+yearmonth.toPlainDate({ day: 15 });
+
+assert.sameValue(calendar.fieldsCallCount, 2, "fields() method called twice");
+assert.compareArray(calendar.fieldsCalledWith[0], expected1, "fields() method called first time with correct args");
+assert.compareArray(calendar.fieldsCalledWith[1], expected2, "fields() method called second time with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole first iterable");
+assert(calendar.iteratorExhausted[1], "iterated through the whole second iterable");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-merge-fields-returns-primitive.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-merge-fields-returns-primitive.js
new file mode 100644
index 00000000000..0b083aaab02
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/calendar-merge-fields-returns-primitive.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: >
+ with() should throw a TypeError if mergeFields() returns a primitive,
+ without passing the value on to any other calendar methods
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[undefined, null, true, 3.14159, "bad value", Symbol("no"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarMergeFieldsReturnsPrimitive(primitive);
+ const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+ assert.throws(TypeError, () => instance.toPlainDate({ day: 2 }), "bad return from mergeFields() throws");
+ assert.sameValue(calendar.dateFromFieldsCallCount, 0, "dateFromFields() never called");
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/copies-merge-fields-object.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/copies-merge-fields-object.js
new file mode 100644
index 00000000000..ae24d79a26c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/copies-merge-fields-object.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: The object returned from mergeFields() is copied before being passed to monthDayFromFields().
+info: |
+ sec-temporal.plainyearmonth.prototype.toplaindate steps 9 and 11:
+ 9. Let _mergedFields_ be ? CalendarMergeFields(_calendar_, _fields_, _inputFields_).
+ 11. Set _mergedFields_ to ? PrepareTemporalFields(_mergedFields_, _mergedFieldNames_, «»).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+ "get day", // first receiver fields, then input fields
+ "get day.valueOf",
+ "call day.valueOf",
+];
+
+const calendar = TemporalHelpers.calendarMergeFieldsGetters();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+yearmonth.toPlainDate({ day: 2 });
+
+assert.compareArray(calendar.mergeFieldsReturnOperations, expected, "getters called on mergeFields return");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..c655904463d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/infinity-throws-rangeerror.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.toPlainDate({ day: inf }), `day property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "day");
+ assert.throws(RangeError, () => instance.toPlainDate({ day: obj }));
+ assert.compareArray(calls, ["get day.valueOf", "call day.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/length.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/length.js
new file mode 100644
index 00000000000..084bd539584
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: Temporal.PlainYearMonth.prototype.toPlainDate.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.toPlainDate, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/name.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/name.js
new file mode 100644
index 00000000000..806de872226
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: Temporal.PlainYearMonth.prototype.toPlainDate.name is "toPlainDate".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.toPlainDate, "name", {
+ value: "toPlainDate",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/not-a-constructor.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/not-a-constructor.js
new file mode 100644
index 00000000000..f29bf78d7ef
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: >
+ Temporal.PlainYearMonth.prototype.toPlainDate does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.toPlainDate();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.toPlainDate), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.toPlainDate)");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/prop-desc.js
new file mode 100644
index 00000000000..e19db789cad
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toPlainDate/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.toplaindate
+description: The "toPlainDate" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.toPlainDate,
+ "function",
+ "`typeof PlainYearMonth.prototype.toPlainDate` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "toPlainDate", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toString/builtin.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toString/builtin.js
new file mode 100644
index 00000000000..ba64c8a5249
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toString/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tostring
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.toString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-invalid-string.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-invalid-string.js
new file mode 100644
index 00000000000..e1b8b710254
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-invalid-string.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.protoype.tostring
+description: RangeError thrown when calendarName option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « String », « *"auto"*, *"always"*, *"never"* », *"auto"*).
+ sec-temporal.plainyearmonth.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+assert.throws(RangeError, () => yearmonth.toString({ calendarName: "other string" }));
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-undefined.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-undefined.js
new file mode 100644
index 00000000000..6478995fc14
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-undefined.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.protoype.tostring
+description: Fallback value for calendarName option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « String », « *"auto"*, *"always"*, *"never"* », *"auto"*).
+ sec-temporal.plainyearmonth.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const calendar = {
+ toString() { return "custom"; }
+};
+const yearmonth1 = new Temporal.PlainYearMonth(2000, 5);
+const yearmonth2 = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+[
+ [yearmonth1, "2000-05"],
+ [yearmonth2, "2000-05-01[u-ca=custom]"],
+].forEach(([yearmonth, expected]) => {
+ const explicit = yearmonth.toString({ calendarName: undefined });
+ assert.sameValue(explicit, expected, "default calendarName option is auto");
+
+ const implicit = yearmonth.toString({});
+ assert.sameValue(implicit, expected, "default calendarName option is auto");
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-wrong-type.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-wrong-type.js
new file mode 100644
index 00000000000..301b62703a9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-wrong-type.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.protoype.tostring
+description: Type conversions for calendarName option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « String », « *"auto"*, *"always"*, *"never"* », *"auto"*).
+ sec-temporal.plainyearmonth.protoype.tostring step 4:
+ 4. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = {
+ toString() { return "custom"; }
+};
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+TemporalHelpers.checkStringOptionWrongType("calendarName", "auto",
+ (calendarName) => yearmonth.toString({ calendarName }),
+ (result, descr) => assert.sameValue(result, "2000-05-01[u-ca=custom]", descr),
+);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toString/length.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toString/length.js
new file mode 100644
index 00000000000..f053b88685e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toString/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tostring
+description: Temporal.PlainYearMonth.prototype.toString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toString/name.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toString/name.js
new file mode 100644
index 00000000000..d60737c473e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toString/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tostring
+description: Temporal.PlainYearMonth.prototype.toString.name is "toString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toString/not-a-constructor.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toString/not-a-constructor.js
new file mode 100644
index 00000000000..3dbe3a98c7d
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toString/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tostring
+description: >
+ Temporal.PlainYearMonth.prototype.toString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.toString), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.toString)");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toString/options-undefined.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toString/options-undefined.js
new file mode 100644
index 00000000000..19b6e8e7b32
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toString/options-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tostring
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const calendar = {
+ toString() { return "custom"; }
+};
+const yearmonth1 = new Temporal.PlainYearMonth(2000, 5);
+const yearmonth2 = new Temporal.PlainYearMonth(2000, 5, calendar);
+
+[
+ [yearmonth1, "2000-05"],
+ [yearmonth2, "2000-05-01[u-ca=custom]"],
+].forEach(([yearmonth, expected]) => {
+ const explicit = yearmonth.toString(undefined);
+ assert.sameValue(explicit, expected, "default calendarName option is auto");
+
+ const implicit = yearmonth.toString();
+ assert.sameValue(implicit, expected, "default calendarName option is auto");
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/toString/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/prototype/toString/prop-desc.js
new file mode 100644
index 00000000000..587c2b97130
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/toString/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tostring
+description: The "toString" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.toString,
+ "function",
+ "`typeof PlainYearMonth.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/builtin.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/builtin.js
new file mode 100644
index 00000000000..2055f6dc26a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.until
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.until),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.until),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.until),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.until.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 00000000000..33e4f9bdf4a
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.plainyearmonth.prototype.until steps 20–21:
+ 20. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _largestUnit_).
+ 21. Let _result_ be ? CalendarDateUntil(_calendar_, _thisDate_, _otherDate_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.PlainYearMonth(2000, 5, calendar);
+ const later = new Temporal.PlainYearMonth(2001, 6, calendar);
+ earlier.until(later, { largestUnit });
+ },
+ {
+ years: ["year"],
+ months: ["month"]
+ }
+);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-fields-iterable.js
new file mode 100644
index 00000000000..e40c6d78bca
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-fields-iterable.js
@@ -0,0 +1,40 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalYearMonth(_other_).
+ sec-temporal.plainyearmonth.prototype.until step 13:
+ 13. Let fieldNames be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal-totemporalyearmonth step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "monthCode",
+ "year",
+];
+const expected2 = [
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+yearmonth.until({ year: 2005, month: 6, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 1, "fields() method not called");
+assert.compareArray(calendar1.fieldsCalledWith[0], expected1, "fields() method called with correct args");
+assert(calendar1.iteratorExhausted[0], "iterated through the whole iterable");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected2, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-temporal-object.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-temporal-object.js
new file mode 100644
index 00000000000..be28ad212b4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plainyearmonth.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalYearMonth(_other_).
+ sec-temporal-totemporalyearmonth step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const yearmonth = new Temporal.PlainYearMonth(2000, 5, temporalObject);
+ yearmonth.until({ year: 2005, month: 6, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..19abebb431e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.prototype.until
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const base = { year: 2000, month: 5 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.until({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.until({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-invalid-string.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-invalid-string.js
new file mode 100644
index 00000000000..e711482a960
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+assert.throws(RangeError, () => earlier.until(later, { largestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-plurals-accepted.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-plurals-accepted.js
new file mode 100644
index 00000000000..d0ba4bfa3c1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-plurals-accepted.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const validUnits = [
+ "year",
+ "month",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => earlier.until(later, { largestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-undefined.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-undefined.js
new file mode 100644
index 00000000000..4222cfe9387
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+const explicit = earlier.until(later, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default largestUnit is year");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-wrong-type.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-wrong-type.js
new file mode 100644
index 00000000000..bba851fc157
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/largestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "month",
+ (largestUnit) => earlier.until(later, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/length.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/length.js
new file mode 100644
index 00000000000..8b382c25ed9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Temporal.PlainYearMonth.prototype.until.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.until, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/name.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/name.js
new file mode 100644
index 00000000000..ffb3a43e5e3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Temporal.PlainYearMonth.prototype.until.name is "until".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.until, "name", {
+ value: "until",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/not-a-constructor.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/not-a-constructor.js
new file mode 100644
index 00000000000..45a782f2b04
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: >
+ Temporal.PlainYearMonth.prototype.until does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.until();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.until), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.until)");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/options-undefined.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/options-undefined.js
new file mode 100644
index 00000000000..6717e477c61
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/options-undefined.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2002, 12);
+
+const explicit = earlier.until(later, undefined);
+assert.sameValue(explicit.years, 2, "default largest unit is years");
+assert.sameValue(explicit.months, 7, "default smallest unit is months and rounding is none");
+
+const implicit = earlier.until(later);
+assert.sameValue(implicit.years, 2, "default largest unit is years");
+assert.sameValue(implicit.months, 7, "default smallest unit is months and rounding is none");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/prop-desc.js
new file mode 100644
index 00000000000..d33ff7c2a91
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: The "until" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.until,
+ "function",
+ "`typeof PlainYearMonth.prototype.until` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "until", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-nan.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-nan.js
new file mode 100644
index 00000000000..a48e5e7d767
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-nan.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plainyearmonth.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: NaN }));
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-non-integer.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-non-integer.js
new file mode 100644
index 00000000000..013e60247d6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-non-integer.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Rounding for roundingIncrement option
+info: |
+ sec-temporal-totemporalroundingincrement step 7:
+ 7. Set _increment_ to floor(ℝ(_increment_)).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2000, 10);
+const result = earlier.until(later, { roundingIncrement: 2.5 });
+TemporalHelpers.assertDuration(result, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, "roundingIncrement 2.5 floors to 2");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-out-of-range.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-out-of-range.js
new file mode 100644
index 00000000000..a7981548252
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-out-of-range.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ sec-temporal-totemporalroundingincrement step 6:
+ 6. If _increment_ < 1 or _increment_ > _maximum_, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2000, 10);
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: Infinity }));
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-undefined.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-undefined.js
new file mode 100644
index 00000000000..a601448bbb8
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plainyearmonth.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+const explicit = earlier.until(later, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingIncrement is 1");
+
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingIncrement is 1");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-wrong-type.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-wrong-type.js
new file mode 100644
index 00000000000..5dfa7203cfe
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingincrement-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plainyearmonth.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, *undefined*, *false*).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => earlier.until(later, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-invalid-string.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-invalid-string.js
new file mode 100644
index 00000000000..3e6231aef4c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "microsecond", roundingMode: "other string" }));
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-undefined.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-undefined.js
new file mode 100644
index 00000000000..c306c434cd3
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-undefined.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 1);
+
+const later1 = new Temporal.PlainYearMonth(2005, 2);
+const explicit1 = earlier.until(later1, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit1 = earlier.until(later1, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+
+const later2 = new Temporal.PlainYearMonth(2005, 12);
+const explicit2 = earlier.until(later2, { smallestUnit: "year", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
+const implicit2 = earlier.until(later2, { smallestUnit: "year" });
+TemporalHelpers.assertDuration(implicit2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, "default roundingMode is trunc");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-wrong-type.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-wrong-type.js
new file mode 100644
index 00000000000..70933207442
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/roundingmode-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => earlier.until(later, { smallestUnit: "year", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-invalid-string.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-invalid-string.js
new file mode 100644
index 00000000000..17bc84c2790
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-plurals-accepted.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-plurals-accepted.js
new file mode 100644
index 00000000000..601667cc938
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-plurals-accepted.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+const validUnits = [
+ "year",
+ "month",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => earlier.until(later, { smallestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-undefined.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-undefined.js
new file mode 100644
index 00000000000..54aa1e8d9f8
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+
+const explicit = earlier.until(later, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default smallestUnit is month");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "default smallestUnit is month");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-wrong-type.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-wrong-type.js
new file mode 100644
index 00000000000..5f47720f4f9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/smallestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.until
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainYearMonth(2000, 5);
+const later = new Temporal.PlainYearMonth(2001, 6);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "year",
+ (smallestUnit) => earlier.until(later, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, descr),
+);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/valueOf/builtin.js b/test/built-ins/Temporal/PlainYearMonth/prototype/valueOf/builtin.js
new file mode 100644
index 00000000000..3c07f9a896f
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/valueOf/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.valueof
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.valueOf
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.valueOf),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.valueOf),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.valueOf),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.valueOf.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/valueOf/length.js b/test/built-ins/Temporal/PlainYearMonth/prototype/valueOf/length.js
new file mode 100644
index 00000000000..70cfee64df8
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/valueOf/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.valueof
+description: Temporal.PlainYearMonth.prototype.valueOf.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.valueOf, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/valueOf/name.js b/test/built-ins/Temporal/PlainYearMonth/prototype/valueOf/name.js
new file mode 100644
index 00000000000..1635cee7ed6
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/valueOf/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.valueof
+description: Temporal.PlainYearMonth.prototype.valueOf.name is "valueOf".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.valueOf, "name", {
+ value: "valueOf",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/valueOf/not-a-constructor.js b/test/built-ins/Temporal/PlainYearMonth/prototype/valueOf/not-a-constructor.js
new file mode 100644
index 00000000000..ef8255fc4b5
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/valueOf/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.valueof
+description: >
+ Temporal.PlainYearMonth.prototype.valueOf does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.valueOf();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.valueOf), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.valueOf)");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/valueOf/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/prototype/valueOf/prop-desc.js
new file mode 100644
index 00000000000..819f12f999b
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/valueOf/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.valueof
+description: The "valueOf" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.valueOf,
+ "function",
+ "`typeof PlainYearMonth.prototype.valueOf` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "valueOf", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/with/builtin.js b/test/built-ins/Temporal/PlainYearMonth/prototype/with/builtin.js
new file mode 100644
index 00000000000..ad64fa7a8b1
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/with/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: >
+ Tests that Temporal.PlainYearMonth.prototype.with
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.with),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.with),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.with),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainYearMonth.prototype.with.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-arguments.js b/test/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-arguments.js
new file mode 100644
index 00000000000..88332be0d18
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-arguments.js
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Correct options value is passed to calendar method
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 5. Let yearMonth be ? Invoke(calendar, "yearMonthFromFields", « fields, options »).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const options = {};
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.sameValue(args[1], options, "args[1]");
+ return super.yearMonthFromFields(...args);
+ }
+}
+const plainYearMonth = new Temporal.PlainYearMonth(2000, 7, new CustomCalendar());
+const result = plainYearMonth.with({ month: 5 }, options);
+TemporalHelpers.assertPlainYearMonth(result, 2000, 5, "M05");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-fields-iterable.js b/test/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-fields-iterable.js
new file mode 100644
index 00000000000..1c5fcb20c48
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-fields-iterable.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.plainyearmonth.prototype.with step 9:
+ 9. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+yearmonth.with({ year: 2005 });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-merge-fields-returns-primitive.js b/test/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-merge-fields-returns-primitive.js
new file mode 100644
index 00000000000..1139d27ee0c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/with/calendar-merge-fields-returns-primitive.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: >
+ with() should throw a TypeError if mergeFields() returns a primitive,
+ without passing the value on to any other calendar methods
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[undefined, null, true, 3.14159, "bad value", Symbol("no"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarMergeFieldsReturnsPrimitive(primitive);
+ const instance = new Temporal.PlainYearMonth(2000, 5, calendar);
+ assert.throws(TypeError, () => instance.with({ year: 2005 }), "bad return from mergeFields() throws");
+ assert.sameValue(calendar.yearMonthFromFieldsCallCount, 0, "yearMonthFromFields() never called");
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/with/copies-merge-fields-object.js b/test/built-ins/Temporal/PlainYearMonth/prototype/with/copies-merge-fields-object.js
new file mode 100644
index 00000000000..7cad0926d88
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/with/copies-merge-fields-object.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: The object returned from mergeFields() is copied before being passed to monthDayFromFields().
+info: |
+ sec-temporal.plainyearmonth.prototype.with steps 13–15:
+ 13. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialYearMonth_).
+ 14. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, «»).
+ 15. Return ? YearMonthFromFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get month",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+];
+
+const calendar = TemporalHelpers.calendarMergeFieldsGetters();
+const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+yearmonth.with({ year: 2004 });
+
+assert.compareArray(calendar.mergeFieldsReturnOperations, expected, "getters called on mergeFields return");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/with/infinity-throws-rangeerror.js b/test/built-ins/Temporal/PlainYearMonth/prototype/with/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..6dbe7c63805
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/with/infinity-throws-rangeerror.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.prototype.with
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.with({ [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.with({ [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/with/length.js b/test/built-ins/Temporal/PlainYearMonth/prototype/with/length.js
new file mode 100644
index 00000000000..64530ee84c9
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/with/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Temporal.PlainYearMonth.prototype.with.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.with, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/with/name.js b/test/built-ins/Temporal/PlainYearMonth/prototype/with/name.js
new file mode 100644
index 00000000000..4162c764a12
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/with/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Temporal.PlainYearMonth.prototype.with.name is "with".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainYearMonth.prototype.with, "name", {
+ value: "with",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/with/not-a-constructor.js b/test/built-ins/Temporal/PlainYearMonth/prototype/with/not-a-constructor.js
new file mode 100644
index 00000000000..efa32a61b11
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/with/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: >
+ Temporal.PlainYearMonth.prototype.with does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainYearMonth.prototype.with();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.with), false,
+ "isConstructor(Temporal.PlainYearMonth.prototype.with)");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/with/options-undefined.js b/test/built-ins/Temporal/PlainYearMonth/prototype/with/options-undefined.js
new file mode 100644
index 00000000000..a6c0c1abceb
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/with/options-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 2);
+const fields = { month: 13 };
+
+const explicit = yearmonth.with(fields, undefined);
+assert.sameValue(explicit.month, 12, "default overflow is constrain");
+
+const implicit = yearmonth.with(fields);
+assert.sameValue(implicit.month, 12, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/with/order-of-operations.js b/test/built-ins/Temporal/PlainYearMonth/prototype/with/order-of-operations.js
new file mode 100644
index 00000000000..f78031bd4f7
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/with/order-of-operations.js
@@ -0,0 +1,48 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Properties on an object passed to with() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5);
+const expected = [
+ "get calendar",
+ "get timeZone",
+ "get month",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+];
+const actual = [];
+const fields = {
+ year: 1.7,
+ month: 1.7,
+ monthCode: "M01",
+};
+const argument = new Proxy(fields, {
+ get(target, key) {
+ actual.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(actual, result, key);
+ },
+ has(target, key) {
+ actual.push(`has ${key}`);
+ return key in target;
+ },
+});
+const result = instance.with(argument);
+TemporalHelpers.assertPlainYearMonth(result, 1, 1, "M01");
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-invalid-string.js b/test/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-invalid-string.js
new file mode 100644
index 00000000000..b02a099ca88
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-invalid-string.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.with step 16:
+ 16. Return ? YearMonthFromFields(_calendar_, _fields_, _options_).
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+assert.throws(RangeError, () => yearmonth.with({ month: 8 }, { overflow: "other string" }));
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-undefined.js b/test/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-undefined.js
new file mode 100644
index 00000000000..e737c261719
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-undefined.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.with step 16:
+ 16. Return ? YearMonthFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+const explicit = yearmonth.with({ month: 15 }, { overflow: undefined });
+TemporalHelpers.assertPlainYearMonth(explicit, 2000, 12, "M12", "default overflow is constrain");
+const implicit = yearmonth.with({ month: 15 }, {});
+TemporalHelpers.assertPlainYearMonth(implicit, 2000, 12, "M12", "default overflow is constrain");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-wrong-type.js b/test/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-wrong-type.js
new file mode 100644
index 00000000000..7abe7387806
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/with/overflow-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-isoyearmonthfromfields step 2:
+ 2. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.plainyearmonth.prototype.with step 16:
+ 16. Return ? YearMonthFromFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const yearmonth = new Temporal.PlainYearMonth(2000, 5);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => yearmonth.with({ month: 8 }, { overflow }),
+ (result, descr) => TemporalHelpers.assertPlainYearMonth(result, 2000, 8, "M08", descr),
+);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/with/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/prototype/with/prop-desc.js
new file mode 100644
index 00000000000..66e39c51fe4
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/with/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: The "with" property of Temporal.PlainYearMonth.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainYearMonth.prototype.with,
+ "function",
+ "`typeof PlainYearMonth.prototype.with` is `function`"
+);
+
+verifyProperty(Temporal.PlainYearMonth.prototype, "with", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/with/subclassing-ignored.js b/test/built-ins/Temporal/PlainYearMonth/prototype/with/subclassing-ignored.js
new file mode 100644
index 00000000000..b7b27961c2e
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/with/subclassing-ignored.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.with
+description: Objects of a subclass are never created as return values for with()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.PlainYearMonth,
+ [2000, 5],
+ "with",
+ [{ month: 11 }],
+ (result) => TemporalHelpers.assertPlainYearMonth(result, 2000, 11, "M11"),
+);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/year/calendar-returns-infinity.js b/test/built-ins/Temporal/PlainYearMonth/prototype/year/calendar-returns-infinity.js
new file mode 100644
index 00000000000..2a16a71ee49
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/year/calendar-returns-infinity.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.year
+description: Getter throws if the calendar returns ±∞ from its year method
+features: [Temporal]
+---*/
+
+class InfinityCalendar extends Temporal.Calendar {
+ constructor(positive) {
+ super("iso8601");
+ this.positive = positive;
+ }
+
+ year() {
+ return this.positive ? Infinity : -Infinity;
+ }
+}
+
+const pos = new InfinityCalendar(true);
+const instance1 = new Temporal.PlainYearMonth(2000, 5, pos);
+assert.throws(RangeError, () => instance1.year);
+
+const neg = new InfinityCalendar(false);
+const instance2 = new Temporal.PlainYearMonth(2000, 5, neg);
+assert.throws(RangeError, () => instance2.year);
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/year/prop-desc.js b/test/built-ins/Temporal/PlainYearMonth/prototype/year/prop-desc.js
new file mode 100644
index 00000000000..c6cfdc2800c
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/year/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.year
+description: The "year" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "year");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/PlainYearMonth/refisoday-undefined.js b/test/built-ins/Temporal/PlainYearMonth/refisoday-undefined.js
new file mode 100644
index 00000000000..95496c77645
--- /dev/null
+++ b/test/built-ins/Temporal/PlainYearMonth/refisoday-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth
+description: referenceISODay argument defaults to 1 if not given
+features: [Temporal]
+---*/
+
+const calendar = new Temporal.Calendar("iso8601");
+const args = [2000, 5, calendar];
+
+const dateExplicit = new Temporal.PlainYearMonth(...args, undefined);
+assert.sameValue(dateExplicit.getISOFields().isoDay, 1, "default referenceISODay is 1");
+
+const dateImplicit = new Temporal.PlainYearMonth(...args);
+assert.sameValue(dateImplicit.getISOFields().isoDay, 1, "default referenceISODay is 1");
diff --git a/test/built-ins/Temporal/TimeZone/basic.js b/test/built-ins/Temporal/TimeZone/basic.js
new file mode 100644
index 00000000000..a803f729b80
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/basic.js
@@ -0,0 +1,34 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone
+description: Basic tests for the Temporal.TimeZone constructor.
+features: [Temporal]
+---*/
+
+const valid = [
+ ["+01:00"],
+ ["-01:00"],
+ ["+0330", "+03:30"],
+ ["-0650", "-06:50"],
+ ["-08", "-08:00"],
+ ["\u221201:00", "-01:00"],
+ ["\u22120650", "-06:50"],
+ ["\u221208", "-08:00"],
+ ["+01:00:00", "+01:00"],
+ ["-010000", "-01:00"],
+ ["+03:30:00.000000001", "+03:30:00.000000001"],
+ ["-033000.1", "-03:30:00.1"],
+ ["UTC"],
+];
+for (const [zone, id = zone] of valid) {
+ const result = new Temporal.TimeZone(zone);
+ assert.sameValue(typeof result, "object", `object should be created for ${zone}`);
+ assert.sameValue(result.id, id, `id for ${zone} should be ${id}`);
+}
+
+const invalid = ["+00:01.1", "-01.1"];
+for (const zone of invalid) {
+ assert.throws(RangeError, () => new Temporal.TimeZone(zone), `should throw for ${zone}`);
+}
diff --git a/test/built-ins/Temporal/TimeZone/builtin.js b/test/built-ins/Temporal/TimeZone/builtin.js
new file mode 100644
index 00000000000..c7f419b1fa5
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/builtin.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone
+description: Tests that Temporal.TimeZone meets the requirements for built-in objects
+info: |
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.TimeZone),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.TimeZone.prototype,
+ "object", "prototype property");
diff --git a/test/built-ins/Temporal/TimeZone/constructor.js b/test/built-ins/Temporal/TimeZone/constructor.js
new file mode 100644
index 00000000000..7ed1159e10a
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/constructor.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone
+description: Temporal.TimeZone constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.TimeZone());
diff --git a/test/built-ins/Temporal/TimeZone/from/argument-object-invalid.js b/test/built-ins/Temporal/TimeZone/from/argument-object-invalid.js
new file mode 100644
index 00000000000..1c49e98deda
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/from/argument-object-invalid.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.from
+description: TimeZone.from() with invalid objects.
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => Temporal.TimeZone.from({ timeZone: "local" }));
+assert.throws(RangeError, () => Temporal.TimeZone.from({ timeZone: { timeZone: "UTC" } }));
diff --git a/test/built-ins/Temporal/TimeZone/from/argument-object.js b/test/built-ins/Temporal/TimeZone/from/argument-object.js
new file mode 100644
index 00000000000..499e9cc67d7
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/from/argument-object.js
@@ -0,0 +1,48 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.from
+description: An object is returned unchanged
+features: [Temporal]
+---*/
+
+class CustomTimeZone extends Temporal.TimeZone {}
+
+const objects = [
+ new Temporal.TimeZone("UTC"),
+ new CustomTimeZone("UTC"),
+ {},
+ { getPlainDateTimeFor: null },
+ { id: "Etc/Custom" },
+];
+
+const thisValues = [
+ Temporal.TimeZone,
+ CustomTimeZone,
+ {},
+ null,
+ undefined,
+ 7,
+];
+
+for (const thisValue of thisValues) {
+ for (const object of objects) {
+ const result = Temporal.TimeZone.from.call(thisValue, object);
+ assert.sameValue(result, object);
+ }
+
+ const zdt = new Temporal.ZonedDateTime(0n, "UTC");
+ const fromZdt = Temporal.TimeZone.from.call(thisValue, zdt);
+ assert.sameValue(fromZdt, zdt.timeZone);
+ assert.sameValue(fromZdt.id, "UTC");
+
+ const tz = new Temporal.TimeZone("UTC");
+ const fromPropertyBagObject = Temporal.TimeZone.from.call(thisValue, { timeZone: tz });
+ assert.sameValue(fromPropertyBagObject, tz);
+ assert.sameValue(fromPropertyBagObject.id, "UTC");
+
+ const fromPropertyBagString = Temporal.TimeZone.from.call(thisValue, { timeZone: "UTC" });
+ assert(fromPropertyBagString instanceof Temporal.TimeZone);
+ assert.sameValue(fromPropertyBagString.id, "UTC");
+}
diff --git a/test/built-ins/Temporal/TimeZone/from/argument-primitive.js b/test/built-ins/Temporal/TimeZone/from/argument-primitive.js
new file mode 100644
index 00000000000..23f35b48a99
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/from/argument-primitive.js
@@ -0,0 +1,45 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.from
+description: RangeError thrown if a value is passed that converts to an invalid string
+features: [Temporal]
+---*/
+
+class CustomTimeZone extends Temporal.TimeZone {}
+
+const primitives = [
+ undefined,
+ null,
+ true,
+ "string",
+ "local",
+ "Z",
+ "-08:00[America/Vancouver]",
+ "+00:01.1",
+ "-01.1",
+ "1994-11-05T08:15:30+25:00",
+ "1994-11-05T13:15:30-25:00",
+ 7,
+ 4.2,
+ 12n,
+];
+
+const thisValues = [
+ Temporal.TimeZone,
+ CustomTimeZone,
+ {},
+ null,
+ undefined,
+ 7,
+];
+
+for (const thisValue of thisValues) {
+ for (const primitive of primitives) {
+ assert.throws(RangeError, () => Temporal.TimeZone.from.call(thisValue, primitive));
+ }
+
+ const symbol = Symbol();
+ assert.throws(TypeError, () => Temporal.TimeZone.from.call(thisValue, symbol));
+}
diff --git a/test/built-ins/Temporal/TimeZone/from/argument-valid.js b/test/built-ins/Temporal/TimeZone/from/argument-valid.js
new file mode 100644
index 00000000000..21c74a77f57
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/from/argument-valid.js
@@ -0,0 +1,34 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.from
+description: Built-in time zones are parsed correctly out of valid strings
+features: [Temporal]
+---*/
+
+const valids = [
+ ["+01:00"],
+ ["-01:00"],
+ ["+0330", "+03:30"],
+ ["-0650", "-06:50"],
+ ["-08", "-08:00"],
+ ["\u221201:00", "-01:00"],
+ ["\u22120650", "-06:50"],
+ ["\u221208", "-08:00"],
+ ["+01:00:00", "+01:00"],
+ ["-010000", "-01:00"],
+ ["+03:30:00.000000001", "+03:30:00.000000001"],
+ ["-033000.1", "-03:30:00.1"],
+ ["UTC"],
+ ["1994-11-05T08:15:30-05:00", "-05:00"],
+ ["1994-11-05T08:15:30\u221205:00", "-05:00"],
+ ["1994-11-05T13:15:30Z", "UTC"],
+];
+
+for (const [valid, canonical = valid] of valids) {
+ const result = Temporal.TimeZone.from(valid);
+ assert.sameValue(Object.getPrototypeOf(result), Temporal.TimeZone.prototype);
+ assert.sameValue(result.id, canonical);
+ assert.sameValue(result.toString(), canonical);
+}
diff --git a/test/built-ins/Temporal/TimeZone/from/builtin.js b/test/built-ins/Temporal/TimeZone/from/builtin.js
new file mode 100644
index 00000000000..d4953fe1f75
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/from/builtin.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.from
+description: Tests that Temporal.TimeZone.from meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.TimeZone.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.TimeZone.from.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/TimeZone/from/length.js b/test/built-ins/Temporal/TimeZone/from/length.js
new file mode 100644
index 00000000000..526772f93ec
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/from/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.from
+description: Temporal.TimeZone.from.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/from/name.js b/test/built-ins/Temporal/TimeZone/from/name.js
new file mode 100644
index 00000000000..9b8ef82cfe3
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/from/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.from
+description: Temporal.TimeZone.from.name is "from"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/from/not-a-constructor.js b/test/built-ins/Temporal/TimeZone/from/not-a-constructor.js
new file mode 100644
index 00000000000..c4ea8fdd1ea
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/from/not-a-constructor.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.from
+description: Temporal.TimeZone.from does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.TimeZone.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.TimeZone.from), false,
+ "isConstructor(Temporal.TimeZone.from)");
diff --git a/test/built-ins/Temporal/TimeZone/from/prop-desc.js b/test/built-ins/Temporal/TimeZone/from/prop-desc.js
new file mode 100644
index 00000000000..42c0e5c3ff7
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/from/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.from
+description: The "from" property of Temporal.TimeZone
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone.from,
+ "function",
+ "`typeof TimeZone.from` is `function`"
+);
+
+verifyProperty(Temporal.TimeZone, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/from/subclassing-ignored.js b/test/built-ins/Temporal/TimeZone/from/subclassing-ignored.js
new file mode 100644
index 00000000000..beb9251068d
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/from/subclassing-ignored.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.TimeZone,
+ "from",
+ ["UTC"],
+ (result) => {
+ assert.sameValue(result.id, "UTC", "id property of result");
+ assert.sameValue(result.toString(), "UTC", "toString() of result");
+ },
+);
diff --git a/test/built-ins/Temporal/TimeZone/from/timezone-string-datetime.js b/test/built-ins/Temporal/TimeZone/from/timezone-string-datetime.js
new file mode 100644
index 00000000000..8b44409daed
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/from/timezone-string-datetime.js
@@ -0,0 +1,42 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.from
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => Temporal.TimeZone.from(timeZone), "bare date-time string is not a time zone");
+assert.throws(RangeError, () => Temporal.TimeZone.from({ timeZone }), "bare date-time string is not a time zone");
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = Temporal.TimeZone.from(timeZone);
+assert.sameValue(result1.id, "UTC", "date-time + Z is UTC time zone");
+const result2 = Temporal.TimeZone.from({ timeZone });
+assert.sameValue(result2.id, "UTC", "date-time + Z is UTC time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result3 = Temporal.TimeZone.from(timeZone);
+assert.sameValue(result3.id, "-07:00", "date-time + offset is the offset time zone");
+const result4 = Temporal.TimeZone.from({ timeZone });
+assert.sameValue(result4.id, "-07:00", "date-time + offset is the offset time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30[America/Vancouver]";
+const result5 = Temporal.TimeZone.from(timeZone);
+assert.sameValue(result5.id, "America/Vancouver", "date-time + IANA annotation is the IANA time zone");
+const result6 = Temporal.TimeZone.from({ timeZone });
+assert.sameValue(result6.id, "America/Vancouver", "date-time + IANA annotation is the IANA time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30Z[America/Vancouver]";
+const result7 = Temporal.TimeZone.from(timeZone);
+assert.sameValue(result7.id, "America/Vancouver", "date-time + Z + IANA annotation is the IANA time zone");
+const result8 = Temporal.TimeZone.from({ timeZone });
+assert.sameValue(result8.id, "America/Vancouver", "date-time + Z + IANA annotation is the IANA time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30-07:00[America/Vancouver]";
+const result9 = Temporal.TimeZone.from(timeZone);
+assert.sameValue(result9.id, "America/Vancouver", "date-time + offset + IANA annotation is the IANA time zone");
+const result10 = Temporal.TimeZone.from({ timeZone });
+assert.sameValue(result10.id, "America/Vancouver", "date-time + offset + IANA annotation is the IANA time zone (string in property bag)");
diff --git a/test/built-ins/Temporal/TimeZone/length.js b/test/built-ins/Temporal/TimeZone/length.js
new file mode 100644
index 00000000000..4f2cc665142
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone
+description: Temporal.TimeZone.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/missing-arguments.js b/test/built-ins/Temporal/TimeZone/missing-arguments.js
new file mode 100644
index 00000000000..c407daed1cb
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/missing-arguments.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone
+description: RangeError thrown when constructor invoked with no argument
+features: [Temporal]
+---*/
+
+assert.throws(RangeError, () => new Temporal.TimeZone());
+assert.throws(RangeError, () => new Temporal.TimeZone(undefined));
diff --git a/test/built-ins/Temporal/TimeZone/name.js b/test/built-ins/Temporal/TimeZone/name.js
new file mode 100644
index 00000000000..632a9505b9d
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone
+description: Temporal.TimeZone.name is "TimeZone"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone, "name", {
+ value: "TimeZone",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prop-desc.js b/test/built-ins/Temporal/TimeZone/prop-desc.js
new file mode 100644
index 00000000000..ace7b1503cd
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone
+description: The "TimeZone" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone,
+ "function",
+ "`typeof TimeZone` is `function`"
+);
+
+verifyProperty(Temporal, "TimeZone", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-not-datetime.js b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-not-datetime.js
new file mode 100644
index 00000000000..caf2725370d
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-not-datetime.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getinstantfor
+description: Appropriate error thrown when argument cannot be converted to Temporal.PlainDateTime
+features: [Temporal]
+---*/
+
+const timeZone = Temporal.TimeZone.from("UTC");
+assert.throws(RangeError, () => timeZone.getInstantFor(undefined), "undefined");
+assert.throws(RangeError, () => timeZone.getInstantFor(null), "null");
+assert.throws(RangeError, () => timeZone.getInstantFor(true), "boolean");
+assert.throws(RangeError, () => timeZone.getInstantFor(""), "empty string");
+assert.throws(TypeError, () => timeZone.getInstantFor(Symbol()), "Symbol");
+assert.throws(RangeError, () => timeZone.getInstantFor(5), "number");
+assert.throws(RangeError, () => timeZone.getInstantFor(5n), "bigint");
+assert.throws(TypeError, () => timeZone.getInstantFor({ year: 2020 }), "plain object");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-plaindate.js b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-plaindate.js
new file mode 100644
index 00000000000..eb15eb6ca4b
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-plaindate.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getinstantfor
+description: Fast path for converting Temporal.PlainDate to Temporal.PlainDateTime by reading internal slots
+info: |
+ sec-temporal.timezone.prototype.getinstantfor step 3:
+ 3. Set _dateTime_ to ? ToTemporalDateTime(_dateTime_).
+ sec-temporal-totemporaldatetime step 2.b:
+ b. If _item_ has an [[InitializedTemporalDate]] internal slot, then
+ i. Return ? CreateTemporalDateTime(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], 0, 0, 0, 0, 0, 0, _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalPlainDateTimeFastPath((date) => {
+ const timezone = new Temporal.TimeZone("UTC");
+ const result = timezone.getInstantFor(date);
+ assert.sameValue(result.epochNanoseconds, 957_225_600_000_000_000n, "epochNanoseconds result");
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-balance-negative-time-units.js b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 00000000000..61a009f4d93
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getinstantfor
+description: Negative time fields are balanced upwards if the argument is given as ZonedDateTime
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-totemporaldatetime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.timezone.prototype.getinstantfor step 3:
+ 3. Set _dateTime_ ? ToTemporalDateTime(_dateTime_).
+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 = new Temporal.TimeZone("-00:00:00.000000002");
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+const conversionTimeZone = new Temporal.TimeZone("UTC"); // should not be used to interpret the argument
+const instant = conversionTimeZone.getInstantFor(datetime);
+
+assert.sameValue(instant.epochNanoseconds, 3661_001_000_999n);
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-negative-epochnanoseconds.js b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 00000000000..891716b420b
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getinstantfor
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const instance = new Temporal.TimeZone("UTC");
+const result = instance.getInstantFor(datetime);
+assert.sameValue(result.epochNanoseconds, -13849764_999_999_999n);
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..fcb0d378624
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getinstantfor
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const builtinTimeZone = new Temporal.TimeZone("UTC");
+ assert.throws(RangeError, () => builtinTimeZone.getInstantFor(datetime));
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..1c2a4704b30
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getinstantfor
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_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 builtinTimeZone = new Temporal.TimeZone("UTC");
+ assert.throws(RangeError, () => builtinTimeZone.getInstantFor(datetime));
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..9aa6e7fc1fc
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getinstantfor
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const builtinTimeZone = new Temporal.TimeZone("UTC");
+ assert.throws(TypeError, () => builtinTimeZone.getInstantFor(datetime));
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/balance-negative-time-units.js b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/balance-negative-time-units.js
new file mode 100644
index 00000000000..9c3056d7946
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/balance-negative-time-units.js
@@ -0,0 +1,45 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getinstantfor
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-addtime step 8:
+ 8. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-adddatetime step 1:
+ 1. Let _timeResult_ be ? AddTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal-builtintimezonegetinstantfor step 13.a:
+ a. Let _earlier_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], 0, 0, 0, 0, 0, 0, 0, 0, 0, −_nanoseconds_, *"constrain"*).
+ sec-temporal.timezone.prototype.getinstantfor step 6:
+ 6. Return ? BuiltinTimeZoneGetInstantFor(_timeZone_, _dateTime_, _disambiguation_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const shiftInstant = new Temporal.Instant(3661_001_001_001n);
+const tz = TemporalHelpers.oneShiftTimeZone(shiftInstant, 2);
+const datetime = new Temporal.PlainDateTime(1970, 1, 1, 1, 1, 1, 1, 1, 1);
+
+// This code path is encountered if disambiguation is `earlier` and the shift is
+// a spring-forward change
+tz.getInstantFor(datetime, { disambiguation: "earlier" });
+
+const expected = [
+ "1970-01-01T01:01:01.001001001",
+ "1970-01-01T01:01:01.001000999",
+];
+assert.compareArray(tz.getPossibleInstantsForCalledWith, expected);
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/builtin.js b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/builtin.js
new file mode 100644
index 00000000000..fac2790e990
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getinstantfor
+description: >
+ Tests that Temporal.TimeZone.prototype.getInstantFor
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.TimeZone.prototype.getInstantFor),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone.prototype.getInstantFor),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone.prototype.getInstantFor),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.TimeZone.prototype.getInstantFor.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/calendar-dateadd-called-with-options-undefined.js b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/calendar-dateadd-called-with-options-undefined.js
new file mode 100644
index 00000000000..4a8f43ced04
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/calendar-dateadd-called-with-options-undefined.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getinstantfor
+description: >
+ BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the
+ options value
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddUndefinedOptions();
+const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9);
+const pdt = new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, 0, calendar);
+
+["earlier", "compatible", "later"].forEach((disambiguation) => {
+ calendar.dateAddCallCount = 0;
+
+ timeZone.getInstantFor(pdt, { disambiguation });
+ assert.sameValue(calendar.dateAddCallCount, 1, `calling with disambiguation ${disambiguation}`);
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/calendar-fields-iterable.js b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/calendar-fields-iterable.js
new file mode 100644
index 00000000000..240e42c2a99
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/calendar-fields-iterable.js
@@ -0,0 +1,37 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getinstantfor
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.timezone.prototype.getinstantfor step 3:
+ 3. Set _dateTime_ to ? ToTemporalDateTime(_dateTime_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "hour",
+ "microsecond",
+ "millisecond",
+ "minute",
+ "month",
+ "monthCode",
+ "nanosecond",
+ "second",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const timeZone = new Temporal.TimeZone("UTC");
+timeZone.getInstantFor({ year: 2000, month: 5, day: 2, calendar });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/calendar-temporal-object.js b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/calendar-temporal-object.js
new file mode 100644
index 00000000000..7f44cd88d1c
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getinstantfor
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.timezone.prototype.getinstantfor step 3:
+ 3. Set _dateTime_ to ? ToTemporalDateTime(_dateTime_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ timeZone.getInstantFor({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/disambiguation-invalid-string.js b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/disambiguation-invalid-string.js
new file mode 100644
index 00000000000..b2de9799a9c
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/disambiguation-invalid-string.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getinstantfor
+description: RangeError thrown when disambiguation option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal.timezone.prototype.getinstantfor step 5:
+ 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2001, 9, 9, 1, 46, 40, 987, 654, 321);
+const timeZone = new Temporal.TimeZone("UTC");
+assert.throws(RangeError, () => timeZone.getInstantFor(datetime, { disambiguation: "other string" }));
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/disambiguation-undefined.js b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/disambiguation-undefined.js
new file mode 100644
index 00000000000..1cfacf1981a
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/disambiguation-undefined.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getinstantfor
+description: Fallback value for disambiguation option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal.timezone.prototype.getinstantfor step 5:
+ 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const springForwardDateTime = new Temporal.PlainDateTime(2000, 4, 2, 2, 30);
+const fallBackDateTime = new Temporal.PlainDateTime(2000, 10, 29, 1, 30);
+
+[
+ [springForwardDateTime, 954671400_000_000_000n],
+ [fallBackDateTime, 972808200_000_000_000n],
+].forEach(([datetime, expected]) => {
+ const explicit = timeZone.getInstantFor(datetime, { disambiguation: undefined });
+ assert.sameValue(explicit.epochNanoseconds, expected, "default disambiguation is compatible");
+ const implicit = timeZone.getInstantFor(datetime, {});
+ assert.sameValue(implicit.epochNanoseconds, expected, "default disambiguation is compatible");
+ const lambda = timeZone.getInstantFor(datetime, () => {});
+ assert.sameValue(lambda.epochNanoseconds, expected, "default disambiguation is compatible");
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/disambiguation-wrong-type.js b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/disambiguation-wrong-type.js
new file mode 100644
index 00000000000..4a8988e55d5
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/disambiguation-wrong-type.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getinstantfor
+description: Type conversions for disambiguation option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal.timezone.prototype.getinstantfor step 5:
+ 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.PlainDateTime(2001, 9, 9, 1, 46, 40, 987, 654, 321);
+const timeZone = new Temporal.TimeZone("UTC");
+TemporalHelpers.checkStringOptionWrongType("disambiguation", "compatible",
+ (disambiguation) => timeZone.getInstantFor(datetime, { disambiguation }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_987_654_321n, descr),
+);
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/infinity-throws-rangeerror.js b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..52c877b2d16
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.timezone.prototype.getinstantfor
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.getInstantFor({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.getInstantFor({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/length.js b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/length.js
new file mode 100644
index 00000000000..4188f547e66
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getinstantfor
+description: Temporal.TimeZone.prototype.getInstantFor.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone.prototype.getInstantFor, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/name.js b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/name.js
new file mode 100644
index 00000000000..9f369768c47
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getinstantfor
+description: Temporal.TimeZone.prototype.getInstantFor.name is "getInstantFor".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone.prototype.getInstantFor, "name", {
+ value: "getInstantFor",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/not-a-constructor.js b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/not-a-constructor.js
new file mode 100644
index 00000000000..1c1429ce136
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getinstantfor
+description: >
+ Temporal.TimeZone.prototype.getInstantFor does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.TimeZone.prototype.getInstantFor();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.TimeZone.prototype.getInstantFor), false,
+ "isConstructor(Temporal.TimeZone.prototype.getInstantFor)");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/options-undefined.js b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/options-undefined.js
new file mode 100644
index 00000000000..350e6863678
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/options-undefined.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getinstantfor
+includes: [temporalHelpers.js]
+description: Verify that undefined options are handled correctly.
+features: [BigInt, Temporal]
+---*/
+
+const datetimeEarlier = new Temporal.PlainDateTime(2000, 10, 29, 1, 34, 56, 987, 654, 321);
+const datetimeLater = new Temporal.PlainDateTime(2000, 4, 2, 2, 34, 56, 987, 654, 321);
+const timeZone = TemporalHelpers.springForwardFallBackTimeZone();
+
+[
+ [datetimeEarlier, 972808496987654321n],
+ [datetimeLater, 954671696987654321n],
+].forEach(([datetime, expected]) => {
+ const explicit = timeZone.getInstantFor(datetime, undefined);
+ assert.sameValue(explicit.epochNanoseconds, expected, "default disambiguation is compatible");
+
+ const implicit = timeZone.getInstantFor(datetime);
+ assert.sameValue(implicit.epochNanoseconds, expected, "default disambiguation is compatible");
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/prop-desc.js b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/prop-desc.js
new file mode 100644
index 00000000000..2eb0f587650
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getinstantfor
+description: The "getInstantFor" property of Temporal.TimeZone.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone.prototype.getInstantFor,
+ "function",
+ "`typeof TimeZone.prototype.getInstantFor` is `function`"
+);
+
+verifyProperty(Temporal.TimeZone.prototype, "getInstantFor", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/read-time-fields-before-datefromfields.js b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/read-time-fields-before-datefromfields.js
new file mode 100644
index 00000000000..6a6fca8c6d2
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getInstantFor/read-time-fields-before-datefromfields.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getinstantfor
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.timezone.prototype.getinstantfor step 3:
+ 3. Set _dateTime_ to ? ToTemporalDateTime(_dateTime_).
+ sec-temporal-totemporaldatetime step 2.e:
+ e. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timezone = new Temporal.TimeZone("UTC");
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const result = timezone.getInstantFor({ year: 1970, month: 1, day: 1, calendar });
+
+assert.sameValue(result.epochNanoseconds, 0n, "epochNanoseconds result");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-zoneddatetime.js b/test/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-zoneddatetime.js
new file mode 100644
index 00000000000..bf351bbd352
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getNextTransition/argument-zoneddatetime.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getnexttransition
+description: Fast path for converting Temporal.ZonedDateTime to Temporal.Instant
+info: |
+ sec-temporal.timezone.prototype.getnexttransition step 3:
+ 3. Set _startingPoint_ to ? ToTemporalInstant(_startingPoint_).
+ sec-temporal-totemporalinstant step 1.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return ! CreateTemporalInstant(_item_.[[Nanoseconds]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalInstantFastPath((datetime) => {
+ const timeZone = Temporal.TimeZone.from("UTC");
+ const result = timeZone.getNextTransition(datetime);
+ assert.sameValue(result, null, "transition result");
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getNextTransition/builtin.js b/test/built-ins/Temporal/TimeZone/prototype/getNextTransition/builtin.js
new file mode 100644
index 00000000000..4964f48b53b
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getNextTransition/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getnexttransition
+description: >
+ Tests that Temporal.TimeZone.prototype.getNextTransition
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.TimeZone.prototype.getNextTransition),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone.prototype.getNextTransition),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone.prototype.getNextTransition),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.TimeZone.prototype.getNextTransition.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getNextTransition/instant-string.js b/test/built-ins/Temporal/TimeZone/prototype/getNextTransition/instant-string.js
new file mode 100644
index 00000000000..cce9d82bae2
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getNextTransition/instant-string.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getnexttransition
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("America/Vancouver");
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.getNextTransition(str), "bare date-time string is not an instant");
+str = "1970-01-01T00:00[America/Vancouver]";
+assert.throws(RangeError, () => instance.getNextTransition(str), "date-time + IANA annotation is not an instant");
+
+// The following are all valid strings so should not throw:
+
+[
+ "1970-01-01T00:00Z",
+ "1970-01-01T00:00+01:00",
+ "1970-01-01T00:00Z[America/Vancouver]",
+ "1970-01-01T00:00+01:00[America/Vancouver]",
+].forEach((str) => instance.getNextTransition(str));
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getNextTransition/length.js b/test/built-ins/Temporal/TimeZone/prototype/getNextTransition/length.js
new file mode 100644
index 00000000000..248f2416f19
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getNextTransition/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getnexttransition
+description: Temporal.TimeZone.prototype.getNextTransition.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone.prototype.getNextTransition, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getNextTransition/name.js b/test/built-ins/Temporal/TimeZone/prototype/getNextTransition/name.js
new file mode 100644
index 00000000000..0fdb93d632c
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getNextTransition/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getnexttransition
+description: Temporal.TimeZone.prototype.getNextTransition.name is "getNextTransition".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone.prototype.getNextTransition, "name", {
+ value: "getNextTransition",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getNextTransition/not-a-constructor.js b/test/built-ins/Temporal/TimeZone/prototype/getNextTransition/not-a-constructor.js
new file mode 100644
index 00000000000..5c57f04f86a
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getNextTransition/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getnexttransition
+description: >
+ Temporal.TimeZone.prototype.getNextTransition does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.TimeZone.prototype.getNextTransition();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.TimeZone.prototype.getNextTransition), false,
+ "isConstructor(Temporal.TimeZone.prototype.getNextTransition)");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getNextTransition/prop-desc.js b/test/built-ins/Temporal/TimeZone/prototype/getNextTransition/prop-desc.js
new file mode 100644
index 00000000000..975d1bfd716
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getNextTransition/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getnexttransition
+description: The "getNextTransition" property of Temporal.TimeZone.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone.prototype.getNextTransition,
+ "function",
+ "`typeof TimeZone.prototype.getNextTransition` is `function`"
+);
+
+verifyProperty(Temporal.TimeZone.prototype, "getNextTransition", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-not-absolute.js b/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-not-absolute.js
new file mode 100644
index 00000000000..1f286cbb24f
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-not-absolute.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetnanosecondsfor
+description: Appropriate error thrown when argument cannot be converted to Temporal.Instant
+features: [Temporal]
+---*/
+
+const timeZone = Temporal.TimeZone.from("UTC");
+assert.throws(RangeError, () => timeZone.getOffsetNanosecondsFor(undefined), "undefined");
+assert.throws(RangeError, () => timeZone.getOffsetNanosecondsFor(null), "null");
+assert.throws(RangeError, () => timeZone.getOffsetNanosecondsFor(true), "boolean");
+assert.throws(RangeError, () => timeZone.getOffsetNanosecondsFor(""), "empty string");
+assert.throws(TypeError, () => timeZone.getOffsetNanosecondsFor(Symbol()), "Symbol");
+assert.throws(RangeError, () => timeZone.getOffsetNanosecondsFor(5), "number");
+assert.throws(RangeError, () => timeZone.getOffsetNanosecondsFor(5n), "bigint");
+assert.throws(RangeError, () => timeZone.getOffsetNanosecondsFor({}), "plain object");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-zoneddatetime.js b/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-zoneddatetime.js
new file mode 100644
index 00000000000..a69dd033415
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/argument-zoneddatetime.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetnanosecondsfor
+description: Fast path for converting Temporal.ZonedDateTime to Temporal.Instant
+info: |
+ sec-temporal.timezone.prototype.getoffsetnanosecondsfor step 3:
+ 3. Set _instant_ to ? ToTemporalInstant(_instant_).
+ sec-temporal-totemporalinstant step 1.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return ! CreateTemporalInstant(_item_.[[Nanoseconds]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalInstantFastPath((datetime) => {
+ const timeZone = Temporal.TimeZone.from("UTC");
+ const result = timeZone.getOffsetNanosecondsFor(datetime);
+ assert.sameValue(result, 0, "offset result");
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/builtin.js b/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/builtin.js
new file mode 100644
index 00000000000..de5637a15a7
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetnanosecondsfor
+description: >
+ Tests that Temporal.TimeZone.prototype.getOffsetNanosecondsFor
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.TimeZone.prototype.getOffsetNanosecondsFor),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone.prototype.getOffsetNanosecondsFor),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone.prototype.getOffsetNanosecondsFor),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.TimeZone.prototype.getOffsetNanosecondsFor.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/instant-string.js b/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/instant-string.js
new file mode 100644
index 00000000000..e6f6d4fc988
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/instant-string.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetnanosecondsfor
+description: Conversion of ISO date-time strings to Temporal.Instant instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.getOffsetNanosecondsFor(str), "bare date-time string is not an instant");
+str = "1970-01-01T00:00[America/Vancouver]";
+assert.throws(RangeError, () => instance.getOffsetNanosecondsFor(str), "date-time + IANA annotation is not an instant");
+
+// The following are all valid strings so should not throw:
+
+const valids = [
+ "1970-01-01T00:00Z",
+ "1970-01-01T00:00+01:00",
+ "1970-01-01T00:00Z[America/Vancouver]",
+ "1970-01-01T00:00+01:00[America/Vancouver]",
+];
+for (const str of valids) {
+ const result = instance.getOffsetNanosecondsFor(str);
+ assert.sameValue(result, 0);
+}
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/length.js b/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/length.js
new file mode 100644
index 00000000000..7cb07fecf70
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetnanosecondsfor
+description: Temporal.TimeZone.prototype.getOffsetNanosecondsFor.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone.prototype.getOffsetNanosecondsFor, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/name.js b/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/name.js
new file mode 100644
index 00000000000..79ef66486f6
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetnanosecondsfor
+description: Temporal.TimeZone.prototype.getOffsetNanosecondsFor.name is "getOffsetNanosecondsFor".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone.prototype.getOffsetNanosecondsFor, "name", {
+ value: "getOffsetNanosecondsFor",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/not-a-constructor.js b/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/not-a-constructor.js
new file mode 100644
index 00000000000..5d9093eee6b
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetnanosecondsfor
+description: >
+ Temporal.TimeZone.prototype.getOffsetNanosecondsFor does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.TimeZone.prototype.getOffsetNanosecondsFor();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.TimeZone.prototype.getOffsetNanosecondsFor), false,
+ "isConstructor(Temporal.TimeZone.prototype.getOffsetNanosecondsFor)");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/prop-desc.js b/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/prop-desc.js
new file mode 100644
index 00000000000..fceac4116d3
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetnanosecondsfor
+description: The "getOffsetNanosecondsFor" property of Temporal.TimeZone.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone.prototype.getOffsetNanosecondsFor,
+ "function",
+ "`typeof TimeZone.prototype.getOffsetNanosecondsFor` is `function`"
+);
+
+verifyProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-not-absolute-getOffsetNanosecondsFor-override.js b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-not-absolute-getOffsetNanosecondsFor-override.js
new file mode 100644
index 00000000000..9922140e4d3
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-not-absolute-getOffsetNanosecondsFor-override.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetstringfor
+description: timeZone.getOffsetNanosecondsFor not called when argument cannot be converted to Temporal.Instant
+features: [Temporal]
+---*/
+
+const timeZone = Temporal.TimeZone.from("UTC");
+let called = false;
+timeZone.getOffsetNanosecondsFor = () => called = true;
+assert.throws(RangeError, () => timeZone.getOffsetStringFor(undefined), "undefined");
+assert.throws(RangeError, () => timeZone.getOffsetStringFor(null), "null");
+assert.throws(RangeError, () => timeZone.getOffsetStringFor(true), "boolean");
+assert.throws(RangeError, () => timeZone.getOffsetStringFor(""), "empty string");
+assert.throws(TypeError, () => timeZone.getOffsetStringFor(Symbol()), "Symbol");
+assert.throws(RangeError, () => timeZone.getOffsetStringFor(5), "number");
+assert.throws(RangeError, () => timeZone.getOffsetStringFor(5n), "bigint");
+assert.throws(RangeError, () => timeZone.getOffsetStringFor({}), "plain object");
+assert.sameValue(called, false);
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-not-absolute.js b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-not-absolute.js
new file mode 100644
index 00000000000..4ffcc863db7
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-not-absolute.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetstringfor
+description: Appropriate error thrown when argument cannot be converted to Temporal.Instant
+features: [Temporal]
+---*/
+
+const timeZone = Temporal.TimeZone.from("UTC");
+assert.throws(RangeError, () => timeZone.getOffsetStringFor(undefined), "undefined");
+assert.throws(RangeError, () => timeZone.getOffsetStringFor(null), "null");
+assert.throws(RangeError, () => timeZone.getOffsetStringFor(true), "boolean");
+assert.throws(RangeError, () => timeZone.getOffsetStringFor(""), "string");
+assert.throws(TypeError, () => timeZone.getOffsetStringFor(Symbol()), "Symbol");
+assert.throws(RangeError, () => timeZone.getOffsetStringFor(5), "number");
+assert.throws(RangeError, () => timeZone.getOffsetStringFor(5n), "bigint");
+assert.throws(RangeError, () => timeZone.getOffsetStringFor({}), "plain object");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-zoneddatetime.js b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-zoneddatetime.js
new file mode 100644
index 00000000000..1169032902c
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/argument-zoneddatetime.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetstringfor
+description: Fast path for converting Temporal.ZonedDateTime to Temporal.Instant
+info: |
+ sec-temporal.timezone.prototype.getoffsetstringfor step 3:
+ 3. Set _instant_ to ? ToTemporalInstant(_instant_).
+ sec-temporal-totemporalinstant step 1.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return ! CreateTemporalInstant(_item_.[[Nanoseconds]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalInstantFastPath((datetime) => {
+ const timeZone = Temporal.TimeZone.from("UTC");
+ const result = timeZone.getOffsetStringFor(datetime);
+ assert.sameValue(result, "+00:00", "offset result");
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/builtin.js b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/builtin.js
new file mode 100644
index 00000000000..9d090b07f39
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetstringfor
+description: >
+ Tests that Temporal.TimeZone.prototype.getOffsetStringFor
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.TimeZone.prototype.getOffsetStringFor),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone.prototype.getOffsetStringFor),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone.prototype.getOffsetStringFor),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.TimeZone.prototype.getOffsetStringFor.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/getOffsetNanosecondsFor-undefined.js b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/getOffsetNanosecondsFor-undefined.js
new file mode 100644
index 00000000000..883b12dcac6
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/getOffsetNanosecondsFor-undefined.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetstringfor
+description: Fall back to calling intrinsic getOffsetNanosecondsFor() when method is deleted
+features: [Temporal]
+---*/
+
+const instant = Temporal.Instant.from("1975-02-02T14:25:36.123456789Z");
+const timeZone = Temporal.TimeZone.from("Europe/Madrid");
+timeZone.getOffsetNanosecondsFor = undefined;
+assert.sameValue(timeZone.getOffsetStringFor(instant), "+01:00");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/instant-string.js b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/instant-string.js
new file mode 100644
index 00000000000..583addf70ac
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/instant-string.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetstringfor
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.getOffsetStringFor(str), "bare date-time string is not an instant");
+str = "1970-01-01T00:00[America/Vancouver]";
+assert.throws(RangeError, () => instance.getOffsetStringFor(str), "date-time + IANA annotation is not an instant");
+
+// The following are all valid strings so should not throw:
+
+const valids = [
+ "1970-01-01T00:00Z",
+ "1970-01-01T00:00+01:00",
+ "1970-01-01T00:00Z[America/Vancouver]",
+ "1970-01-01T00:00+01:00[America/Vancouver]",
+];
+for (const str of valids) {
+ const result = instance.getOffsetStringFor(str);
+ assert.sameValue(result, "+00:00");
+}
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/length.js b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/length.js
new file mode 100644
index 00000000000..f8bb1d7b0b3
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetstringfor
+description: Temporal.TimeZone.prototype.getOffsetStringFor.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone.prototype.getOffsetStringFor, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/name.js b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/name.js
new file mode 100644
index 00000000000..0be05ed49d2
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetstringfor
+description: Temporal.TimeZone.prototype.getOffsetStringFor.name is "getOffsetStringFor".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone.prototype.getOffsetStringFor, "name", {
+ value: "getOffsetStringFor",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/not-a-constructor.js b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/not-a-constructor.js
new file mode 100644
index 00000000000..a2b65b5f4a1
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetstringfor
+description: >
+ Temporal.TimeZone.prototype.getOffsetStringFor does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.TimeZone.prototype.getOffsetStringFor();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.TimeZone.prototype.getOffsetStringFor), false,
+ "isConstructor(Temporal.TimeZone.prototype.getOffsetStringFor)");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/prop-desc.js b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/prop-desc.js
new file mode 100644
index 00000000000..7840a891755
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetstringfor
+description: The "getOffsetStringFor" property of Temporal.TimeZone.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone.prototype.getOffsetStringFor,
+ "function",
+ "`typeof TimeZone.prototype.getOffsetStringFor` is `function`"
+);
+
+verifyProperty(Temporal.TimeZone.prototype, "getOffsetStringFor", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..e9c1270dfd6
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetstringfor
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ assert.throws(RangeError, () => timeZone.getOffsetStringFor(instant));
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..f5690f73f47
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetstringfor
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ assert.throws(RangeError, () => timeZone.getOffsetStringFor(instant));
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..4fd73d5c513
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getOffsetStringFor/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetstringfor
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ assert.throws(TypeError, () => timeZone.getOffsetStringFor(instant));
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-negative-epochnanoseconds.js b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-negative-epochnanoseconds.js
new file mode 100644
index 00000000000..a87df234357
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-negative-epochnanoseconds.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getplaindatetimefor
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instant = new Temporal.Instant(-13849764_999_999_999n);
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const instance = new Temporal.TimeZone("UTC");
+const result = instance.getPlainDateTimeFor(instant);
+TemporalHelpers.assertPlainDateTime(result, 1969, 7, "M07", 24, 16, 50, 35, 0, 0, 1);
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-not-absolute-getOffsetNanosecondsFor-override.js b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-not-absolute-getOffsetNanosecondsFor-override.js
new file mode 100644
index 00000000000..ffc3816506e
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-not-absolute-getOffsetNanosecondsFor-override.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getplaindatetimefor
+description: timeZone.getOffsetNanosecondsFor not called when argument cannot be converted to Temporal.Instant
+features: [Temporal]
+---*/
+
+const timeZone = Temporal.TimeZone.from("UTC");
+let called = false;
+timeZone.getOffsetNanosecondsFor = () => called = true;
+assert.throws(RangeError, () => timeZone.getPlainDateTimeFor(undefined), "undefined");
+assert.throws(RangeError, () => timeZone.getPlainDateTimeFor(null), "null");
+assert.throws(RangeError, () => timeZone.getPlainDateTimeFor(true), "boolean");
+assert.throws(RangeError, () => timeZone.getPlainDateTimeFor(""), "empty string");
+assert.throws(TypeError, () => timeZone.getPlainDateTimeFor(Symbol()), "Symbol");
+assert.throws(RangeError, () => timeZone.getPlainDateTimeFor(5), "number");
+assert.throws(RangeError, () => timeZone.getPlainDateTimeFor(5n), "bigint");
+assert.throws(RangeError, () => timeZone.getPlainDateTimeFor({}), "plain object");
+assert.sameValue(called, false);
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-not-absolute.js b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-not-absolute.js
new file mode 100644
index 00000000000..93867b6a271
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-not-absolute.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getplaindatetimefor
+description: Appropriate error thrown when argument cannot be converted to Temporal.Instant
+features: [Temporal]
+---*/
+
+const timeZone = Temporal.TimeZone.from("UTC");
+assert.throws(RangeError, () => timeZone.getPlainDateTimeFor(undefined), "undefined");
+assert.throws(RangeError, () => timeZone.getPlainDateTimeFor(null), "null");
+assert.throws(RangeError, () => timeZone.getPlainDateTimeFor(true), "boolean");
+assert.throws(RangeError, () => timeZone.getPlainDateTimeFor(""), "empty string");
+assert.throws(TypeError, () => timeZone.getPlainDateTimeFor(Symbol()), "Symbol");
+assert.throws(RangeError, () => timeZone.getPlainDateTimeFor(5), "number");
+assert.throws(RangeError, () => timeZone.getPlainDateTimeFor(5n), "bigint");
+assert.throws(RangeError, () => timeZone.getPlainDateTimeFor({}), "plain object");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-zoneddatetime.js b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-zoneddatetime.js
new file mode 100644
index 00000000000..a9e8551b417
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/argument-zoneddatetime.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getplaindatetimefor
+description: Fast path for converting Temporal.ZonedDateTime to Temporal.Instant
+info: |
+ sec-temporal.timezone.prototype.getplaindatetimefor step 2:
+ 2. Set _instant_ to ? ToTemporalInstant(_instant_).
+ sec-temporal-totemporalinstant step 1.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return ! CreateTemporalInstant(_item_.[[Nanoseconds]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalInstantFastPath((datetime) => {
+ const timeZone = Temporal.TimeZone.from("UTC");
+ const result = timeZone.getPlainDateTimeFor(datetime);
+ TemporalHelpers.assertPlainDateTime(result, 2001, 9, "M09", 9, 1, 46, 40, 987, 654, 321);
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/balance-negative-time-units.js b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/balance-negative-time-units.js
new file mode 100644
index 00000000000..02af26a36ba
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/balance-negative-time-units.js
@@ -0,0 +1,39 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getplaindatetimefor
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-get-temporal.timezone.prototype.getplaindatetimefor step 4:
+ 4. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// exact time's epoch parts
+const tz = new Temporal.TimeZone("-00:00:00.000000002");
+const instant = new Temporal.Instant(1001n);
+
+const pdt = tz.getPlainDateTimeFor(instant);
+
+TemporalHelpers.assertPlainDateTime(pdt, 1970, 1, "M01", 1, 0, 0, 0, 0, 0, 999);
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/builtin.js b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/builtin.js
new file mode 100644
index 00000000000..26ce3f02fbd
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getplaindatetimefor
+description: >
+ Tests that Temporal.TimeZone.prototype.getPlainDateTimeFor
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.TimeZone.prototype.getPlainDateTimeFor),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone.prototype.getPlainDateTimeFor),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone.prototype.getPlainDateTimeFor),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.TimeZone.prototype.getPlainDateTimeFor.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-temporal-object.js b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-temporal-object.js
new file mode 100644
index 00000000000..acd488cf7f4
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-temporal-object.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.instant.prototype.tozoneddatetime
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.instant.prototype.tozoneddatetime step 6:
+ 6. Let _calendar_ be ? ToTemporalCalendar(_calendarLike_).
+ 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 instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ const timezone = new Temporal.TimeZone("UTC");
+ const result = timezone.getPlainDateTimeFor(instant, temporalObject);
+ assert.sameValue(result.calendar, calendar, "Temporal object coerced to calendar");
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-undefined.js b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-undefined.js
new file mode 100644
index 00000000000..92add0be1c8
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-undefined.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getplaindatetimefor
+description: Calendar argument defaults to the built-in ISO 8601 calendar
+features: [Temporal]
+---*/
+
+const instant = Temporal.Instant.from("1975-02-02T14:25:36.123456789Z");
+const timeZone = Temporal.TimeZone.from("UTC");
+
+Object.defineProperty(Temporal.Calendar, "from", {
+ get() {
+ throw new Test262Error("Should not call Calendar.from");
+ },
+});
+
+const result1 = timeZone.getPlainDateTimeFor(instant);
+assert.sameValue(result1.calendar.toString(), "iso8601");
+
+const result2 = timeZone.getPlainDateTimeFor(instant, undefined);
+assert.sameValue(result2.calendar.toString(), "iso8601");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/custom-timezone.js b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/custom-timezone.js
new file mode 100644
index 00000000000..7b831f0f118
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/custom-timezone.js
@@ -0,0 +1,37 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getplaindatetimefor
+description: getOffsetNanosecondsFor is called by getPlainDateTimeFor
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "get timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+];
+
+const instant = Temporal.Instant.from("1975-02-02T14:25:36.123456789Z");
+const timeZone = new Proxy({
+ getOffsetNanosecondsFor(instantArg) {
+ actual.push("call timeZone.getOffsetNanosecondsFor");
+ assert.sameValue(instantArg, instant);
+ return 9876543210123;
+ },
+}, {
+ has(target, property) {
+ actual.push(`has timeZone.${property}`);
+ return property in target;
+ },
+ get(target, property) {
+ actual.push(`get timeZone.${property}`);
+ return target[property];
+ },
+});
+
+const result = Temporal.TimeZone.prototype.getPlainDateTimeFor.call(timeZone, instant);
+TemporalHelpers.assertPlainDateTime(result, 1975, 2, "M02", 2, 17, 10, 12, 666, 666, 912);
+assert.compareArray(actual, expected);
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/getOffsetNanosecondsFor-undefined.js b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/getOffsetNanosecondsFor-undefined.js
new file mode 100644
index 00000000000..cf4ad976fe8
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/getOffsetNanosecondsFor-undefined.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getplaindatetimefor
+description: Fall back to calling intrinsic getOffsetNanosecondsFor() when method is deleted
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instant = Temporal.Instant.from("1975-02-02T14:25:36.123456789Z");
+const timeZone = Temporal.TimeZone.from("Europe/Madrid");
+timeZone.getOffsetNanosecondsFor = undefined;
+const result = timeZone.getPlainDateTimeFor(instant);
+TemporalHelpers.assertPlainDateTime(result, 1975, 2, "M02", 2, 15, 25, 36, 123, 456, 789);
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/instant-string.js b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/instant-string.js
new file mode 100644
index 00000000000..fbbd9f5c11b
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/instant-string.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getplaindatetimefor
+description: Conversion of ISO date-time strings to Temporal.Instant instances
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.getPlainDateTimeFor(str), "bare date-time string is not an instant");
+str = "1970-01-01T00:00[America/Vancouver]";
+assert.throws(RangeError, () => instance.getPlainDateTimeFor(str), "date-time + IANA annotation is not an instant");
+
+str = "1970-01-01T00:00Z";
+const result1 = instance.getPlainDateTimeFor(str);
+TemporalHelpers.assertPlainDateTime(result1, 1970, 1, "M01", 1, 0, 0, 0, 0, 0, 0, "date-time + Z preserves exact time");
+
+str = "1970-01-01T00:00+01:00";
+const result2 = instance.getPlainDateTimeFor(str);
+TemporalHelpers.assertPlainDateTime(result2, 1969, 12, "M12", 31, 23, 0, 0, 0, 0, 0, "date-time + offset preserves exact time with offset");
+
+str = "1970-01-01T00:00Z[America/Vancouver]";
+const result3 = instance.getPlainDateTimeFor(str);
+TemporalHelpers.assertPlainDateTime(result3, 1970, 1, "M01", 1, 0, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation ignores the IANA annotation");
+
+str = "1970-01-01T00:00+01:00[America/Vancouver]";
+const result4 = instance.getPlainDateTimeFor(str);
+TemporalHelpers.assertPlainDateTime(result4, 1969, 12, "M12", 31, 23, 0, 0, 0, 0, 0, "date-time + offset + IANA annotation ignores the IANA annotation");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/length.js b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/length.js
new file mode 100644
index 00000000000..e30cab7528a
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getplaindatetimefor
+description: Temporal.TimeZone.prototype.getPlainDateTimeFor.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone.prototype.getPlainDateTimeFor, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/name.js b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/name.js
new file mode 100644
index 00000000000..5e0b9b08af9
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getplaindatetimefor
+description: Temporal.TimeZone.prototype.getPlainDateTimeFor.name is "getPlainDateTimeFor".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone.prototype.getPlainDateTimeFor, "name", {
+ value: "getPlainDateTimeFor",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/not-a-constructor.js b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/not-a-constructor.js
new file mode 100644
index 00000000000..4c5226e1708
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getplaindatetimefor
+description: >
+ Temporal.TimeZone.prototype.getPlainDateTimeFor does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.TimeZone.prototype.getPlainDateTimeFor();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.TimeZone.prototype.getPlainDateTimeFor), false,
+ "isConstructor(Temporal.TimeZone.prototype.getPlainDateTimeFor)");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/pre-epoch.js b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/pre-epoch.js
new file mode 100644
index 00000000000..4dddb892bb6
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/pre-epoch.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getplaindatetimefor
+description: Test of basic functionality for an exact time earlier than the Unix epoch
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instant = Temporal.Instant.from("1969-07-16T13:32:01.234567891Z");
+assert.sameValue(instant.toString(), "1969-07-16T13:32:01.234567891Z");
+const timeZone = Temporal.TimeZone.from("-04:00");
+const dateTime = timeZone.getPlainDateTimeFor(instant);
+TemporalHelpers.assertPlainDateTime(dateTime, 1969, 7, "M07", 16, 9, 32, 1, 234, 567, 891);
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/prop-desc.js b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/prop-desc.js
new file mode 100644
index 00000000000..fb9003568ca
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getplaindatetimefor
+description: The "getPlainDateTimeFor" property of Temporal.TimeZone.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone.prototype.getPlainDateTimeFor,
+ "function",
+ "`typeof TimeZone.prototype.getPlainDateTimeFor` is `function`"
+);
+
+verifyProperty(Temporal.TimeZone.prototype, "getPlainDateTimeFor", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..e6c3ef76238
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getplaindatetimefor
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ assert.throws(RangeError, () => timeZone.getPlainDateTimeFor(instant));
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..c50b8ede4f8
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getplaindatetimefor
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ assert.throws(RangeError, () => timeZone.getPlainDateTimeFor(instant));
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..eb2bb198bdf
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getplaindatetimefor
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const instant = new Temporal.Instant(1_000_000_000_987_654_321n);
+ assert.throws(TypeError, () => timeZone.getPlainDateTimeFor(instant));
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-not-datetime.js b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-not-datetime.js
new file mode 100644
index 00000000000..f6107979367
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-not-datetime.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getpossibleinstantsfor
+description: Appropriate error thrown when argument cannot be converted to Temporal.PlainDateTime
+features: [Temporal]
+---*/
+
+const timeZone = Temporal.TimeZone.from("UTC");
+assert.throws(RangeError, () => timeZone.getPossibleInstantsFor(undefined), "undefined");
+assert.throws(RangeError, () => timeZone.getPossibleInstantsFor(null), "null");
+assert.throws(RangeError, () => timeZone.getPossibleInstantsFor(true), "boolean");
+assert.throws(RangeError, () => timeZone.getPossibleInstantsFor(""), "empty string");
+assert.throws(TypeError, () => timeZone.getPossibleInstantsFor(Symbol()), "Symbol");
+assert.throws(RangeError, () => timeZone.getPossibleInstantsFor(5), "number");
+assert.throws(RangeError, () => timeZone.getPossibleInstantsFor(5n), "bigint");
+assert.throws(TypeError, () => timeZone.getPossibleInstantsFor({}), "plain object");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-plaindate.js b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-plaindate.js
new file mode 100644
index 00000000000..d916b5c4997
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-plaindate.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.until
+description: Fast path for converting Temporal.PlainDate to Temporal.PlainDateTime by reading internal slots
+info: |
+ sec-temporal.timezone.prototype.getpossibleinstantsfor step 3:
+ 3. Set _dateTime_ to ? ToTemporalDateTime(_dateTime_).
+ sec-temporal-totemporaldatetime step 2.b:
+ b. If _item_ has an [[InitializedTemporalDate]] internal slot, then
+ i. Return ? CreateTemporalDateTime(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], 0, 0, 0, 0, 0, 0, _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalPlainDateTimeFastPath((date) => {
+ const timezone = new Temporal.TimeZone("UTC");
+ const result = timezone.getPossibleInstantsFor(date);
+ assert.sameValue(result.length, 1, "one possible instant");
+ assert.sameValue(result[0].epochNanoseconds, 957_225_600_000_000_000n, "epochNanoseconds result");
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-balance-negative-time-units.js b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 00000000000..7b5b97cf529
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,44 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getpossibleinstantsfor
+description: Negative time fields are balanced upwards if the argument is given as ZonedDateTime
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-totemporaldatetime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.timezone.prototype.getpossibleinstantsfor step 3:
+ 3. Set _dateTime_ ? ToTemporalDateTime(_dateTime_).
+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 = new Temporal.TimeZone("-00:00:00.000000002");
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+const conversionTimeZone = new Temporal.TimeZone("UTC"); // should not be used to interpret the argument
+const instants = conversionTimeZone.getPossibleInstantsFor(datetime);
+
+assert.sameValue(instants.length, 1);
+assert.sameValue(instants[0].epochNanoseconds, 3661_001_000_999n);
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-negative-epochnanoseconds.js b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 00000000000..6b77502afde
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getpossibleinstantsfor
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [compareArray.js]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const instance = new Temporal.TimeZone("UTC");
+const result = instance.getPossibleInstantsFor(datetime);
+assert.compareArray(result.map((i) => i.epochNanoseconds), [-13849764_999_999_999n]);
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..962bf65e230
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getpossibleinstantsfor
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const builtinTimeZone = new Temporal.TimeZone("UTC");
+ assert.throws(RangeError, () => builtinTimeZone.getPossibleInstantsFor(datetime));
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..47a00eb45ec
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getpossibleinstantsfor
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_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 builtinTimeZone = new Temporal.TimeZone("UTC");
+ assert.throws(RangeError, () => builtinTimeZone.getPossibleInstantsFor(datetime));
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..f6a2a9cd7bd
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getpossibleinstantsfor
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const builtinTimeZone = new Temporal.TimeZone("UTC");
+ assert.throws(TypeError, () => builtinTimeZone.getPossibleInstantsFor(datetime));
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/builtin.js b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/builtin.js
new file mode 100644
index 00000000000..05623e37a20
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getpossibleinstantsfor
+description: >
+ Tests that Temporal.TimeZone.prototype.getPossibleInstantsFor
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.TimeZone.prototype.getPossibleInstantsFor),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone.prototype.getPossibleInstantsFor),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone.prototype.getPossibleInstantsFor),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.TimeZone.prototype.getPossibleInstantsFor.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/calendar-fields-iterable.js b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/calendar-fields-iterable.js
new file mode 100644
index 00000000000..c5010034a8b
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/calendar-fields-iterable.js
@@ -0,0 +1,37 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getpossibleinstantsfor
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.timezone.prototype.getpossibleinstantsfor step 3:
+ 3. Set _dateTime_ to ? ToTemporalDateTime(_dateTime_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "hour",
+ "microsecond",
+ "millisecond",
+ "minute",
+ "month",
+ "monthCode",
+ "nanosecond",
+ "second",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const timeZone = new Temporal.TimeZone("UTC");
+timeZone.getPossibleInstantsFor({ year: 2000, month: 5, day: 2, calendar });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/calendar-temporal-object.js b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/calendar-temporal-object.js
new file mode 100644
index 00000000000..1e81176a1dd
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getpossibleinstantsfor
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.timezone.prototype.getpossibleinstantsfor step 3:
+ 3. Set _dateTime_ to ? ToTemporalDateTime(_dateTime_).
+ sec-temporal-totemporaldatetime step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ timeZone.getPossibleInstantsFor({ year: 2000, month: 5, day: 2, calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/infinity-throws-rangeerror.js b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..371891da0cf
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.timezone.prototype.getpossibleinstantsfor
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.getPossibleInstantsFor({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.getPossibleInstantsFor({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/length.js b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/length.js
new file mode 100644
index 00000000000..4be7b6d8519
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getpossibleinstantsfor
+description: Temporal.TimeZone.prototype.getPossibleInstantsFor.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone.prototype.getPossibleInstantsFor, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/name.js b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/name.js
new file mode 100644
index 00000000000..59d0a384bdb
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getpossibleinstantsfor
+description: Temporal.TimeZone.prototype.getPossibleInstantsFor.name is "getPossibleInstantsFor".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone.prototype.getPossibleInstantsFor, "name", {
+ value: "getPossibleInstantsFor",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/not-a-constructor.js b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/not-a-constructor.js
new file mode 100644
index 00000000000..2f3b6de5cda
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getpossibleinstantsfor
+description: >
+ Temporal.TimeZone.prototype.getPossibleInstantsFor does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.TimeZone.prototype.getPossibleInstantsFor();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.TimeZone.prototype.getPossibleInstantsFor), false,
+ "isConstructor(Temporal.TimeZone.prototype.getPossibleInstantsFor)");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/prop-desc.js b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/prop-desc.js
new file mode 100644
index 00000000000..4ab7bba6a90
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getpossibleinstantsfor
+description: The "getPossibleInstantsFor" property of Temporal.TimeZone.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone.prototype.getPossibleInstantsFor,
+ "function",
+ "`typeof TimeZone.prototype.getPossibleInstantsFor` is `function`"
+);
+
+verifyProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/read-time-fields-before-datefromfields.js b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/read-time-fields-before-datefromfields.js
new file mode 100644
index 00000000000..fd9cfc93691
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/read-time-fields-before-datefromfields.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getpossibleinstantsfor
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.timezone.prototype.getpossibleinstantsfor step 3:
+ 3. Set _dateTime_ to ? ToTemporalDateTime(_dateTime_).
+ sec-temporal-totemporaldatetime step 2.e:
+ e. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timezone = new Temporal.TimeZone("UTC");
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const result = timezone.getPossibleInstantsFor({ year: 1970, month: 1, day: 1, calendar });
+
+assert.sameValue(result.length, 1, "result array length");
+assert.sameValue(result[0].epochNanoseconds, 0n, "epochNanoseconds result");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-zoneddatetime.js b/test/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-zoneddatetime.js
new file mode 100644
index 00000000000..25b517f90a8
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/argument-zoneddatetime.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getprevioustransition
+description: Fast path for converting Temporal.ZonedDateTime to Temporal.Instant
+info: |
+ sec-temporal.timezone.prototype.getprevioustransition step 3:
+ 3. Set _startingPoint_ to ? ToTemporalInstant(_startingPoint_).
+ sec-temporal-totemporalinstant step 1.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return ! CreateTemporalInstant(_item_.[[Nanoseconds]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalInstantFastPath((datetime) => {
+ const timeZone = Temporal.TimeZone.from("UTC");
+ const result = timeZone.getPreviousTransition(datetime);
+ assert.sameValue(result, null, "transition result");
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/builtin.js b/test/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/builtin.js
new file mode 100644
index 00000000000..4fc7ff1c311
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getprevioustransition
+description: >
+ Tests that Temporal.TimeZone.prototype.getPreviousTransition
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.TimeZone.prototype.getPreviousTransition),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone.prototype.getPreviousTransition),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone.prototype.getPreviousTransition),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.TimeZone.prototype.getPreviousTransition.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/instant-string.js b/test/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/instant-string.js
new file mode 100644
index 00000000000..15dab5701cd
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/instant-string.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getprevioustransition
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("America/Vancouver");
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.getPreviousTransition(str), "bare date-time string is not an instant");
+str = "1970-01-01T00:00[America/Vancouver]";
+assert.throws(RangeError, () => instance.getPreviousTransition(str), "date-time + IANA annotation is not an instant");
+
+// The following are all valid strings so should not throw:
+
+[
+ "1970-01-01T00:00Z",
+ "1970-01-01T00:00+01:00",
+ "1970-01-01T00:00Z[America/Vancouver]",
+ "1970-01-01T00:00+01:00[America/Vancouver]",
+].forEach((str) => instance.getPreviousTransition(str));
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/length.js b/test/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/length.js
new file mode 100644
index 00000000000..83cb3c8abe2
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getprevioustransition
+description: Temporal.TimeZone.prototype.getPreviousTransition.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone.prototype.getPreviousTransition, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/name.js b/test/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/name.js
new file mode 100644
index 00000000000..0dc22a2a50e
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getprevioustransition
+description: Temporal.TimeZone.prototype.getPreviousTransition.name is "getPreviousTransition".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone.prototype.getPreviousTransition, "name", {
+ value: "getPreviousTransition",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/not-a-constructor.js b/test/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/not-a-constructor.js
new file mode 100644
index 00000000000..12f2d39e183
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getprevioustransition
+description: >
+ Temporal.TimeZone.prototype.getPreviousTransition does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.TimeZone.prototype.getPreviousTransition();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.TimeZone.prototype.getPreviousTransition), false,
+ "isConstructor(Temporal.TimeZone.prototype.getPreviousTransition)");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/prop-desc.js b/test/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/prop-desc.js
new file mode 100644
index 00000000000..a2e3c75e5bb
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/getPreviousTransition/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getprevioustransition
+description: The "getPreviousTransition" property of Temporal.TimeZone.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone.prototype.getPreviousTransition,
+ "function",
+ "`typeof TimeZone.prototype.getPreviousTransition` is `function`"
+);
+
+verifyProperty(Temporal.TimeZone.prototype, "getPreviousTransition", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/id/no-toString.js b/test/built-ins/Temporal/TimeZone/prototype/id/no-toString.js
new file mode 100644
index 00000000000..d9d457a1c42
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/id/no-toString.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.timezone.prototype.id
+description: TypeError thrown when toString property not present
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "get timeZone.toString",
+];
+
+const timeZone = new Temporal.TimeZone("UTC");
+Object.defineProperty(timeZone, "toString", {
+ get() {
+ actual.push("get timeZone.toString");
+ return undefined;
+ },
+});
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "id");
+assert.throws(TypeError, () => descriptor.get.call(timeZone));
+assert.compareArray(actual, expected);
diff --git a/test/built-ins/Temporal/TimeZone/prototype/id/plain-custom-timezone.js b/test/built-ins/Temporal/TimeZone/prototype/id/plain-custom-timezone.js
new file mode 100644
index 00000000000..d5cf02a59be
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/id/plain-custom-timezone.js
@@ -0,0 +1,45 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.timezone.prototype.id
+description: Getter calls toString() and returns its value
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "get timeZone[@@toPrimitive]",
+ "get timeZone.toString",
+ "call timeZone.toString",
+];
+
+const timeZone = new Proxy({
+ toString() {
+ actual.push("call timeZone.toString");
+ return "time zone";
+ },
+}, {
+ has(target, property) {
+ if (property === Symbol.toPrimitive) {
+ actual.push('has timeZone[@@toPrimitive]');
+ } else {
+ actual.push(`has timeZone.${property}`);
+ }
+ return property in target;
+ },
+ get(target, property) {
+ if (property === Symbol.toPrimitive) {
+ actual.push('get timeZone[@@toPrimitive]');
+ } else {
+ actual.push(`get timeZone.${property}`);
+ }
+ return target[property];
+ },
+});
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "id");
+const result = descriptor.get.call(timeZone);
+assert.compareArray(actual, expected);
+assert.sameValue(result, "time zone");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/id/prop-desc.js b/test/built-ins/Temporal/TimeZone/prototype/id/prop-desc.js
new file mode 100644
index 00000000000..1887699b543
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/id/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.timezone.prototype.id
+description: The "id" property of Temporal.TimeZone.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "id");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/TimeZone/prototype/toJSON/builtin.js b/test/built-ins/Temporal/TimeZone/prototype/toJSON/builtin.js
new file mode 100644
index 00000000000..37cecb9b122
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/toJSON/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.tojson
+description: >
+ Tests that Temporal.TimeZone.prototype.toJSON
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.TimeZone.prototype.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.TimeZone.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/toJSON/length.js b/test/built-ins/Temporal/TimeZone/prototype/toJSON/length.js
new file mode 100644
index 00000000000..295171903c7
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/toJSON/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.tojson
+description: Temporal.TimeZone.prototype.toJSON.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone.prototype.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/toJSON/name.js b/test/built-ins/Temporal/TimeZone/prototype/toJSON/name.js
new file mode 100644
index 00000000000..cdf784682fa
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/toJSON/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.tojson
+description: Temporal.TimeZone.prototype.toJSON.name is "toJSON".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone.prototype.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/toJSON/not-a-constructor.js b/test/built-ins/Temporal/TimeZone/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 00000000000..24c036a5444
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/toJSON/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.tojson
+description: >
+ Temporal.TimeZone.prototype.toJSON does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.TimeZone.prototype.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.TimeZone.prototype.toJSON), false,
+ "isConstructor(Temporal.TimeZone.prototype.toJSON)");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/toJSON/prop-desc.js b/test/built-ins/Temporal/TimeZone/prototype/toJSON/prop-desc.js
new file mode 100644
index 00000000000..e3d09cec62e
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/toJSON/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.tojson
+description: The "toJSON" property of Temporal.TimeZone.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone.prototype.toJSON,
+ "function",
+ "`typeof TimeZone.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.TimeZone.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/toJSON/tostring-call.js b/test/built-ins/Temporal/TimeZone/prototype/toJSON/tostring-call.js
new file mode 100644
index 00000000000..537a7c3e15f
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/toJSON/tostring-call.js
@@ -0,0 +1,47 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.tojson
+description: toJSON() calls toString() and returns its value
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ 'get timeZone[@@toPrimitive]',
+ 'get timeZone.toString',
+ 'call timeZone.toString',
+];
+
+const timeZone = new Proxy(
+ {
+ toString() {
+ actual.push(`call timeZone.toString`);
+ return 'Etc/TAI';
+ }
+ },
+ {
+ has(target, property) {
+ if (property === Symbol.toPrimitive) {
+ actual.push('has timeZone[@@toPrimitive]');
+ } else {
+ actual.push(`has timeZone.${property}`);
+ }
+ return property in target;
+ },
+ get(target, property) {
+ if (property === Symbol.toPrimitive) {
+ actual.push('get timeZone[@@toPrimitive]');
+ } else {
+ actual.push(`get timeZone.${property}`);
+ }
+ return target[property];
+ }
+ }
+);
+
+const result = Temporal.TimeZone.prototype.toJSON.call(timeZone);
+assert.sameValue(result, 'Etc/TAI', 'toString');
+assert.compareArray(actual, expected);
diff --git a/test/built-ins/Temporal/TimeZone/prototype/toJSON/tostring-undefined-custom.js b/test/built-ins/Temporal/TimeZone/prototype/toJSON/tostring-undefined-custom.js
new file mode 100644
index 00000000000..d3411cbd6ea
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/toJSON/tostring-undefined-custom.js
@@ -0,0 +1,43 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.tojson
+description: TypeError thrown when toString property not present
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ 'get timeZone[@@toPrimitive]',
+ 'get timeZone.toString',
+ 'get timeZone.valueOf',
+];
+
+const timeZone = new Proxy(
+ {
+ toString: undefined
+ },
+ {
+ has(target, property) {
+ if (property === Symbol.toPrimitive) {
+ actual.push('has timeZone[@@toPrimitive]');
+ } else {
+ actual.push(`has timeZone.${property}`);
+ }
+ return property in target;
+ },
+ get(target, property) {
+ if (property === Symbol.toPrimitive) {
+ actual.push('get timeZone[@@toPrimitive]');
+ } else {
+ actual.push(`get timeZone.${property}`);
+ }
+ return target[property];
+ }
+ }
+);
+
+assert.throws(TypeError, () => Temporal.TimeZone.prototype.toJSON.call(timeZone));
+assert.compareArray(actual, expected);
diff --git a/test/built-ins/Temporal/TimeZone/prototype/toJSON/tostring-undefined.js b/test/built-ins/Temporal/TimeZone/prototype/toJSON/tostring-undefined.js
new file mode 100644
index 00000000000..c0a9f067412
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/toJSON/tostring-undefined.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.tojson
+description: TypeError thrown when toString property not present
+features: [Temporal]
+---*/
+
+const tz = Temporal.TimeZone.from('UTC');
+tz.toString = undefined;
+
+assert.throws(TypeError, () => tz.toJSON());
diff --git a/test/built-ins/Temporal/TimeZone/prototype/toString/builtin.js b/test/built-ins/Temporal/TimeZone/prototype/toString/builtin.js
new file mode 100644
index 00000000000..8cd8e1f2fbd
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/toString/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.tostring
+description: >
+ Tests that Temporal.TimeZone.prototype.toString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.TimeZone.prototype.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.TimeZone.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/toString/length.js b/test/built-ins/Temporal/TimeZone/prototype/toString/length.js
new file mode 100644
index 00000000000..f32b3738c1c
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/toString/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.tostring
+description: Temporal.TimeZone.prototype.toString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone.prototype.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/toString/name.js b/test/built-ins/Temporal/TimeZone/prototype/toString/name.js
new file mode 100644
index 00000000000..53bbdc2b208
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/toString/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.tostring
+description: Temporal.TimeZone.prototype.toString.name is "toString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.TimeZone.prototype.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/TimeZone/prototype/toString/not-a-constructor.js b/test/built-ins/Temporal/TimeZone/prototype/toString/not-a-constructor.js
new file mode 100644
index 00000000000..a379a7dcb1f
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/toString/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.tostring
+description: >
+ Temporal.TimeZone.prototype.toString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.TimeZone.prototype.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.TimeZone.prototype.toString), false,
+ "isConstructor(Temporal.TimeZone.prototype.toString)");
diff --git a/test/built-ins/Temporal/TimeZone/prototype/toString/prop-desc.js b/test/built-ins/Temporal/TimeZone/prototype/toString/prop-desc.js
new file mode 100644
index 00000000000..2b117165a68
--- /dev/null
+++ b/test/built-ins/Temporal/TimeZone/prototype/toString/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.tostring
+description: The "toString" property of Temporal.TimeZone.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.TimeZone.prototype.toString,
+ "function",
+ "`typeof TimeZone.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.TimeZone.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/builtin.js b/test/built-ins/Temporal/ZonedDateTime/builtin.js
new file mode 100644
index 00000000000..bff1dfeb5d2
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/builtin.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime
+description: Tests that Temporal.ZonedDateTime meets the requirements for built-in objects
+info: |
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(typeof Temporal.ZonedDateTime.prototype,
+ "object", "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/calendar-temporal-object.js b/test/built-ins/Temporal/ZonedDateTime/calendar-temporal-object.js
new file mode 100644
index 00000000000..d20494190ef
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/calendar-temporal-object.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.zoneddatetime step 5:
+ 5. Let _calendar_ be ? ToTemporalCalendarWithISODefault(_calendarLike_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const result = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", temporalObject);
+ assert.sameValue(result.calendar, calendar, "Temporal object coerced to calendar");
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/calendar-undefined.js b/test/built-ins/Temporal/ZonedDateTime/calendar-undefined.js
new file mode 100644
index 00000000000..baaf93c7f0a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/calendar-undefined.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime
+description: Calendar argument defaults to the built-in ISO 8601 calendar
+features: [BigInt, Temporal]
+---*/
+
+const args = [957270896987654321n, new Temporal.TimeZone("UTC")];
+
+Object.defineProperty(Temporal.Calendar, "from", {
+ get() {
+ throw new Test262Error("Should not get Calendar.from");
+ },
+});
+
+const explicit = new Temporal.ZonedDateTime(...args, undefined);
+assert.sameValue(explicit.calendar.toString(), "iso8601");
+
+const implicit = new Temporal.ZonedDateTime(...args);
+assert.sameValue(implicit.calendar.toString(), "iso8601");
diff --git a/test/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..e0fe6289b22
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.compare
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, Infinity, -Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, hour: 12, timeZone }, datetime));
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(datetime, { year: 2000, month: 5, day: 2, hour: 12, timeZone }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..281afb2447b
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.compare
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, hour: 12, timeZone }, datetime));
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(datetime, { year: 2000, month: 5, day: 2, hour: 12, timeZone }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..1902b882780
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.compare
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+
+ assert.throws(TypeError, () => Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, hour: 12, timeZone }, datetime));
+ assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(datetime, { year: 2000, month: 5, day: 2, hour: 12, timeZone }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/compare/builtin.js b/test/built-ins/Temporal/ZonedDateTime/compare/builtin.js
new file mode 100644
index 00000000000..44f02cbfa9b
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/compare/builtin.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.compare
+description: Tests that Temporal.ZonedDateTime.compare meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.compare),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.compare),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.compare),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.compare.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/compare/calendar-fields-iterable.js b/test/built-ins/Temporal/ZonedDateTime/compare/calendar-fields-iterable.js
new file mode 100644
index 00000000000..f900f9d1770
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/compare/calendar-fields-iterable.js
@@ -0,0 +1,44 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.compare
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.zoneddatetime.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalZonedDateTime(_one_).
+ 2. Set _two_ to ? ToTemporalZonedDateTime(_two_).
+ sec-temporal-totemporalzoneddatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "hour",
+ "microsecond",
+ "millisecond",
+ "minute",
+ "month",
+ "monthCode",
+ "nanosecond",
+ "second",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+Temporal.ZonedDateTime.compare(
+ { year: 2000, month: 5, day: 2, timeZone: "UTC", calendar: calendar1 },
+ { year: 2001, month: 6, day: 3, timeZone: "UTC", calendar: calendar2 },
+);
+
+assert.sameValue(calendar1.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar1.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar1.iteratorExhausted[0], "iterated through the whole iterable");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/ZonedDateTime/compare/calendar-temporal-object.js b/test/built-ins/Temporal/ZonedDateTime/compare/calendar-temporal-object.js
new file mode 100644
index 00000000000..6cf61b5fdef
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/compare/calendar-temporal-object.js
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.compare
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.zoneddatetime.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalZonedDateTime(_one_).
+ 2. Set _two_ to ? ToTemporalZonedDateTime(_two_).
+ sec-temporal-totemporalzoneddatetime step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ Temporal.ZonedDateTime.compare(
+ { year: 2000, month: 5, day: 2, timeZone: "UTC", calendar: temporalObject },
+ { year: 2001, month: 6, day: 3, timeZone: "UTC", calendar: temporalObject },
+ );
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/compare/infinity-throws-rangeerror.js b/test/built-ins/Temporal/ZonedDateTime/compare/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..fd26103e5c6
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/compare/infinity-throws-rangeerror.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in a property bag for either argument is Infinity or -Infinity
+esid: sec-temporal.zoneddatetime.compare
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const other = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.compare({ ...base, [prop]: inf }, other), `${prop} property cannot be ${inf}`);
+
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(other, { ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls1 = [];
+ const obj1 = TemporalHelpers.toPrimitiveObserver(calls1, inf, prop);
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.compare({ ...base, [prop]: obj1 }, other));
+ assert.compareArray(calls1, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+
+ const calls2 = [];
+ const obj2 = TemporalHelpers.toPrimitiveObserver(calls2, inf, prop);
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(other, { ...base, [prop]: obj2 }));
+ assert.compareArray(calls2, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/compare/length.js b/test/built-ins/Temporal/ZonedDateTime/compare/length.js
new file mode 100644
index 00000000000..f2e41f0321d
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/compare/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.compare
+description: Temporal.ZonedDateTime.compare.length is 2
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.compare, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/compare/name.js b/test/built-ins/Temporal/ZonedDateTime/compare/name.js
new file mode 100644
index 00000000000..96e65896712
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/compare/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.compare
+description: Temporal.ZonedDateTime.compare.name is "compare"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.compare, "name", {
+ value: "compare",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/compare/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/compare/not-a-constructor.js
new file mode 100644
index 00000000000..b11d5ab7dee
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/compare/not-a-constructor.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.compare
+description: Temporal.ZonedDateTime.compare does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.compare();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.compare), false,
+ "isConstructor(Temporal.ZonedDateTime.compare)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/compare/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/compare/prop-desc.js
new file mode 100644
index 00000000000..b879fc7e578
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/compare/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.compare
+description: The "compare" property of Temporal.ZonedDateTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.compare,
+ "function",
+ "`typeof ZonedDateTime.compare` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime, "compare", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/compare/read-time-fields-before-datefromfields.js b/test/built-ins/Temporal/ZonedDateTime/compare/read-time-fields-before-datefromfields.js
new file mode 100644
index 00000000000..02f194566a5
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/compare/read-time-fields-before-datefromfields.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.compare
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.zoneddatetime.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalZonedDateTime(_one_).
+ 2. Set _two_ to ? ToTemporalZonedDateTime(_two_).
+ sec-temporal-totemporalzoneddatetime step 2.j:
+ j. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const result = Temporal.ZonedDateTime.compare(
+ { year: 2000, month: 5, day: 2, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC", calendar },
+ { year: 2000, month: 5, day: 2, hour: 6, minute: 54, second: 32, millisecond: 123, microsecond: 456, nanosecond: 789, timeZone: "UTC", calendar },
+);
+
+// will be 0 if the time fields are coerced to their max values due to Infinity
+assert.sameValue(result, 1, "comparison result");
diff --git a/test/built-ins/Temporal/ZonedDateTime/compare/timezone-getpossibleinstantsfor-iterable.js b/test/built-ins/Temporal/ZonedDateTime/compare/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 00000000000..bf31d074551
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/compare/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.compare
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.zoneddatetime.compare steps 1–2:
+ 1. Set _one_ to ? ToTemporalZonedDateTime(_one_).
+ 2. Set _two_ to ? ToTemporalZonedDateTime(_two_).
+ sec-temporal-totemporalzoneddatetime step 7:
+ 7. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "2000-05-02T00:00:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ Temporal.ZonedDateTime.compare(
+ { year: 2000, month: 5, day: 2, timeZone },
+ { year: 2001, month: 6, day: 3, timeZone: "UTC" },
+ );
+}, expected1);
+
+// Same, but on the other operand
+
+const expected2 = [
+ "2001-06-03T00:00:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ Temporal.ZonedDateTime.compare(
+ { year: 2000, month: 5, day: 2, timeZone: "UTC" },
+ { year: 2001, month: 6, day: 3, timeZone },
+ );
+}, expected2);
diff --git a/test/built-ins/Temporal/ZonedDateTime/compare/timezone-string-datetime.js b/test/built-ins/Temporal/ZonedDateTime/compare/timezone-string-datetime.js
new file mode 100644
index 00000000000..fed63db25f7
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/compare/timezone-string-datetime.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.compare
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, timeZone }, instance), "bare date-time string is not a time zone (arg 1)");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(instance, { year: 2000, month: 5, day: 2, timeZone }), "bare date-time string is not a time zone (arg 2)");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, timeZone: { timeZone } }, instance), "bare date-time string is not a time zone (arg 1)");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(instance, { year: 2000, month: 5, day: 2, timeZone: { timeZone } }), "bare date-time string is not a time zone (arg 2)");
+
+// The following are all valid strings so should not throw:
+
+[
+ "2021-08-19T17:30Z",
+ "2021-08-19T17:30-07:00",
+ "2021-08-19T17:30[America/Vancouver]",
+ "2021-08-19T17:30Z[America/Vancouver]",
+ "2021-08-19T17:30-07:00[America/Vancouver]",
+].forEach((timeZone) => {
+ Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, timeZone }, instance);
+ Temporal.ZonedDateTime.compare(instance, { year: 2000, month: 5, day: 2, timeZone });
+ Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, timeZone: { timeZone } }, instance);
+ Temporal.ZonedDateTime.compare(instance, { year: 2000, month: 5, day: 2, timeZone: { timeZone } });
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/compare/zoneddatetime-string.js b/test/built-ins/Temporal/ZonedDateTime/compare/zoneddatetime-string.js
new file mode 100644
index 00000000000..b9fb92ca3d6
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/compare/zoneddatetime-string.js
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.compare
+description: Conversion of ISO date-time strings to Temporal.ZonedDateTime instances
+features: [Temporal]
+---*/
+
+const epoch = new Temporal.ZonedDateTime(0n, "UTC");
+const hourBefore = new Temporal.ZonedDateTime(-3600_000_000_000n, "UTC");
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(str, epoch), "bare date-time string is not a ZonedDateTime (first argument)");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(epoch, str), "bare date-time string is not a ZonedDateTime (second argument)");
+str = "1970-01-01T00:00Z";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(str, epoch), "date-time + Z is not a ZonedDateTime (first argument)");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(epoch, str), "date-time + Z is not a ZonedDateTime (second argument)");
+str = "1970-01-01T00:00+01:00";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(str, epoch), "date-time + offset is not a ZonedDateTime (first argument)");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(epoch, str), "date-time + offset is not a ZonedDateTime (second argument)");
+
+str = "1970-01-01T00:00[Europe/Berlin]";
+const result1 = Temporal.ZonedDateTime.compare(str, hourBefore);
+assert.sameValue(result1, 0, "date-time + IANA annotation preserves wall time in the time zone (first argument)");
+const result2 = Temporal.ZonedDateTime.compare(hourBefore, str);
+assert.sameValue(result2, 0, "date-time + IANA annotation preserves wall time in the time zone (second argument)");
+
+str = "1970-01-01T00:00Z[Europe/Berlin]";
+const result3 = Temporal.ZonedDateTime.compare(str, epoch);
+assert.sameValue(result3, 0, "date-time + Z + IANA annotation preserves exact time in the time zone (first argument)");
+const result4 = Temporal.ZonedDateTime.compare(epoch, str);
+assert.sameValue(result4, 0, "date-time + Z + IANA annotation preserves exact time in the time zone (second argument)");
+
+str = "1970-01-01T00:00+01:00[Europe/Berlin]";
+const result5 = Temporal.ZonedDateTime.compare(str, hourBefore);
+assert.sameValue(result5, 0, "date-time + offset + IANA annotation ensures both exact and wall time match (first argument)");
+const result6 = Temporal.ZonedDateTime.compare(hourBefore, str);
+assert.sameValue(result6, 0, "date-time + offset + IANA annotation ensures both exact and wall time match (second argument)");
+
+str = "1970-01-01T00:00-04:15[Europe/Berlin]";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(str, epoch), "date-time + offset + IANA annotation throws if wall time and exact time mismatch (first argument)");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(epoch, str), "date-time + offset + IANA annotation throws if wall time and exact time mismatch (second argument)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/constructor.js b/test/built-ins/Temporal/ZonedDateTime/constructor.js
new file mode 100644
index 00000000000..54f848296b4
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/constructor.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime
+description: Temporal.ZonedDateTime constructor cannot be called as a function
+info: |
+ 1. If NewTarget is undefined, throw a TypeError exception.
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => Temporal.ZonedDateTime());
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..180242a629e
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, timeZone }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..34d748a9116
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, timeZone }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..5d611a1e550
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+
+ assert.throws(TypeError, () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, timeZone }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/balance-negative-time-units.js b/test/built-ins/Temporal/ZonedDateTime/from/balance-negative-time-units.js
new file mode 100644
index 00000000000..80eb5903e6c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/balance-negative-time-units.js
@@ -0,0 +1,88 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-addtime step 8:
+ 8. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-adddatetime step 1:
+ 1. Let _timeResult_ be ? AddTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal-builtintimezonegetinstantfor step 13.a:
+ a. Let _earlier_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], 0, 0, 0, 0, 0, 0, 0, 0, 0, −_nanoseconds_, *"constrain"*).
+ sec-temporal-interpretisodatetimeoffset steps 4–10:
+ 4. If _offsetNanoseconds_ is *null*, or _offset_ is *"ignore"*, then
+ a. Let _instant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _dateTime_, _disambiguation_).
+ ...
+ ...
+ 6. Assert: _offset_ is *"prefer"* or *"reject"*.
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ ...
+ 9. If _offset_ is *"reject"*, throw a *RangeError* exception.
+ 10. Let _instant_ be ? DisambiguatePossibleInstants(_possibleInstants_, _timeZone_, _dateTime_, _disambiguation_).
+ sec-temporal-totemporalzoneddatetime step 7:
+ 7. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_).
+ sec-temporal.zoneddatetime.from step 3:
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const shiftInstant = new Temporal.Instant(3661_001_001_001n);
+const tz1 = TemporalHelpers.oneShiftTimeZone(shiftInstant, 2);
+
+// This code path is encountered if offset is `ignore` or `prefer`,
+// disambiguation is `earlier` and the shift is a spring-forward change
+Temporal.ZonedDateTime.from({
+ year: 1970,
+ month: 1,
+ day: 1,
+ hour: 1,
+ minute: 1,
+ second: 1,
+ millisecond: 1,
+ microsecond: 1,
+ nanosecond: 1,
+ timeZone: tz1,
+}, { offset: "ignore", disambiguation: "earlier" });
+
+const expected1 = [
+ "1970-01-01T01:01:01.001001001",
+ "1970-01-01T01:01:01.001000999",
+];
+assert.compareArray(tz1.getPossibleInstantsForCalledWith, expected1);
+
+const tz2 = TemporalHelpers.oneShiftTimeZone(shiftInstant, 2);
+
+Temporal.ZonedDateTime.from({
+ year: 1970,
+ month: 1,
+ day: 1,
+ hour: 1,
+ minute: 1,
+ second: 1,
+ millisecond: 1,
+ microsecond: 1,
+ nanosecond: 1,
+ timeZone: tz2,
+}, { offset: "prefer", disambiguation: "earlier" });
+
+const expected2 = [
+ "1970-01-01T01:01:01.001001001",
+ "1970-01-01T01:01:01.001000999",
+];
+assert.compareArray(tz2.getPossibleInstantsForCalledWith, expected2);
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/builtin.js b/test/built-ins/Temporal/ZonedDateTime/from/builtin.js
new file mode 100644
index 00000000000..d1e1cdf24c1
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/builtin.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: Tests that Temporal.ZonedDateTime.from meets the requirements for built-in objects
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.from),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.from),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.from),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.from.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/calendar-fields-iterable.js b/test/built-ins/Temporal/ZonedDateTime/from/calendar-fields-iterable.js
new file mode 100644
index 00000000000..8285f4d67ed
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/calendar-fields-iterable.js
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.zoneddatetime.from step 3:
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+ sec-temporal-totemporalzoneddatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "hour",
+ "microsecond",
+ "millisecond",
+ "minute",
+ "month",
+ "monthCode",
+ "nanosecond",
+ "second",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone: "UTC", calendar });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/calendar-temporal-object.js b/test/built-ins/Temporal/ZonedDateTime/from/calendar-temporal-object.js
new file mode 100644
index 00000000000..60ffe57b212
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.zoneddatetime.from step 3:
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+ sec-temporal-totemporalzoneddatetime step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const result = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone: "UTC", calendar: temporalObject });
+ assert.sameValue(result.calendar, calendar, "Temporal object coerced to calendar");
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/disambiguation-invalid-string.js b/test/built-ins/Temporal/ZonedDateTime/from/disambiguation-invalid-string.js
new file mode 100644
index 00000000000..b9529653e0d
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/disambiguation-invalid-string.js
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: RangeError thrown when disambiguation option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal-totemporalzoneddatetime step 5:
+ 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+ sec-temporal.zoneddatetime.from step 2:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. ...
+ b. Perform ? ToTemporalDisambiguation(_options_).
+ c. ...
+ d. Return ...
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(datetime, { disambiguation: "other string" }));
+
+const timeZone = new Temporal.TimeZone("UTC");
+const propertyBag = { timeZone, year: 2001, month: 9, day: 9, hour: 1, minute: 46, second: 40, millisecond: 987, microsecond: 654, nanosecond: 321 };
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { disambiguation: "other string" }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/disambiguation-undefined.js b/test/built-ins/Temporal/ZonedDateTime/from/disambiguation-undefined.js
new file mode 100644
index 00000000000..8c420894bcc
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/disambiguation-undefined.js
@@ -0,0 +1,35 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: Fallback value for disambiguation option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal-totemporalzoneddatetime step 5:
+ 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+ sec-temporal.zoneddatetime.from step 2:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ d. Return ...
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const springForwardFields = { timeZone, year: 2000, month: 4, day: 2, hour: 2, minute: 30 };
+const fallBackFields = { timeZone, year: 2000, month: 10, day: 29, hour: 1, minute: 30 };
+
+[
+ [springForwardFields, 954671400_000_000_000n],
+ [fallBackFields, 972808200_000_000_000n],
+].forEach(([fields, expected]) => {
+ const explicit = Temporal.ZonedDateTime.from(fields, { disambiguation: undefined });
+ assert.sameValue(explicit.epochNanoseconds, expected, "default disambiguation is compatible (later)");
+
+ // See options-undefined.js for {}
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/disambiguation-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/from/disambiguation-wrong-type.js
new file mode 100644
index 00000000000..f997510dc8d
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/disambiguation-wrong-type.js
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: Type conversions for disambiguation option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal-totemporalzoneddatetime step 5:
+ 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+ sec-temporal.zoneddatetime.from step 2:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. ...
+ b. Perform ? ToTemporalDisambiguation(_options_).
+ c. ...
+ d. Return ...
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("disambiguation", "compatible",
+ (disambiguation) => Temporal.ZonedDateTime.from(datetime, { disambiguation }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_987_654_321n, descr),
+);
+
+const timeZone = new Temporal.TimeZone("UTC");
+const propertyBag = { timeZone, year: 2001, month: 9, day: 9, hour: 1, minute: 46, second: 40, millisecond: 987, microsecond: 654, nanosecond: 321 };
+TemporalHelpers.checkStringOptionWrongType("disambiguation", "compatible",
+ (disambiguation) => Temporal.ZonedDateTime.from(propertyBag, { disambiguation }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_987_654_321n, descr),
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/infinity-throws-rangeerror.js b/test/built-ins/Temporal/ZonedDateTime/from/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..4d992b7eb8b
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/infinity-throws-rangeerror.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.zoneddatetime.from
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ ...base, [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ ...base, [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/length.js b/test/built-ins/Temporal/ZonedDateTime/from/length.js
new file mode 100644
index 00000000000..1bb28877271
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: Temporal.ZonedDateTime.from.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.from, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/name.js b/test/built-ins/Temporal/ZonedDateTime/from/name.js
new file mode 100644
index 00000000000..cd838d3f3aa
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: Temporal.ZonedDateTime.from.name is "from"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.from, "name", {
+ value: "from",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/from/not-a-constructor.js
new file mode 100644
index 00000000000..0616a3c1a5b
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/not-a-constructor.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: Temporal.ZonedDateTime.from does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.from();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.from), false,
+ "isConstructor(Temporal.ZonedDateTime.from)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/offset-invalid-string.js b/test/built-ins/Temporal/ZonedDateTime/from/offset-invalid-string.js
new file mode 100644
index 00000000000..730cbc0147e
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/offset-invalid-string.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: RangeError thrown when offset option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloffset step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"prefer"*, *"use"*, *"ignore"*, *"reject"* », _fallback_).
+ sec-temporal-totemporalzoneddatetime step 6:
+ 6. Let _offset_ be ? ToTemporalOffset(_options_, *"reject"*).
+ sec-temporal.zoneddatetime.from step 2:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ c. Perform ? ToTemporalOffset(_options_, *"reject"*).
+ d. Return ...
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(datetime, { offset: "other string" }));
+
+const timeZone = new Temporal.TimeZone("UTC");
+const propertyBag = { timeZone, year: 2001, month: 9, day: 9, hour: 1, minute: 46, second: 40, millisecond: 987, microsecond: 654, nanosecond: 321 };
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { offset: "other string" }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/offset-undefined.js b/test/built-ins/Temporal/ZonedDateTime/from/offset-undefined.js
new file mode 100644
index 00000000000..1939303ec08
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/offset-undefined.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: Fallback value for offset option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloffset step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"prefer"*, *"use"*, *"ignore"*, *"reject"* », _fallback_).
+ sec-temporal-totemporalzoneddatetime step 6:
+ 6. Let _offset_ be ? ToTemporalOffset(_options_, *"reject"*).
+ sec-temporal.zoneddatetime.from step 2:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ c. Perform ? ToTemporalOffset(_options_, *"reject"*).
+ d. Return ...
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("-04:00");
+const propertyBag = { timeZone, offset: "+01:00", year: 2020, month: 2, day: 16, hour: 23, minute: 45 };
+
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { offset: undefined }), "default offset is reject");
+// See options-undefined.js for {}
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/offset-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/from/offset-wrong-type.js
new file mode 100644
index 00000000000..3544cb820a2
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/offset-wrong-type.js
@@ -0,0 +1,35 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: Type conversions for offset option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloffset step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"prefer"*, *"use"*, *"ignore"*, *"reject"* », _fallback_).
+ sec-temporal-totemporalzoneddatetime step 6:
+ 6. Let _offset_ be ? ToTemporalOffset(_options_, *"reject"*).
+ sec-temporal.zoneddatetime.from step 2:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ c. Perform ? ToTemporalOffset(_options_, *"reject"*).
+ d. Return ...
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("offset", "reject",
+ (disambiguation) => Temporal.ZonedDateTime.from(datetime, { disambiguation }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_987_654_321n, descr),
+);
+
+const timeZone = new Temporal.TimeZone("UTC");
+const propertyBag = { timeZone, offset: "+00:00", year: 2001, month: 9, day: 9, hour: 1, minute: 46, second: 40, millisecond: 987, microsecond: 654, nanosecond: 321 };
+TemporalHelpers.checkStringOptionWrongType("offset", "reject",
+ (disambiguation) => Temporal.ZonedDateTime.from(propertyBag, { disambiguation }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_987_654_321n, descr),
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/options-undefined.js b/test/built-ins/Temporal/ZonedDateTime/from/options-undefined.js
new file mode 100644
index 00000000000..a8a8e3ebf5e
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/options-undefined.js
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+includes: [temporalHelpers.js]
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const overflowFields = { year: 2000, month: 13, day: 2, timeZone: "UTC" };
+
+const overflowExplicit = Temporal.ZonedDateTime.from(overflowFields, undefined);
+assert.sameValue(overflowExplicit.month, 12, "default overflow is constrain");
+
+const overflowPropertyImplicit = Temporal.ZonedDateTime.from(overflowFields, {});
+assert.sameValue(overflowPropertyImplicit.month, 12, "default overflow is constrain");
+
+const overflowImplicit = Temporal.ZonedDateTime.from(overflowFields);
+assert.sameValue(overflowImplicit.month, 12, "default overflow is constrain");
+
+const timeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const disambiguationEarlierFields = { timeZone, year: 2000, month: 10, day: 29, hour: 1, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 };
+const disambiguationLaterFields = { timeZone, year: 2000, month: 4, day: 2, hour: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 };
+
+[
+ [disambiguationEarlierFields, 972808496987654321n],
+ [disambiguationLaterFields, 954671696987654321n],
+].forEach(([fields, expected]) => {
+ const explicit = Temporal.ZonedDateTime.from(fields, undefined);
+ assert.sameValue(explicit.epochNanoseconds, expected, "default disambiguation is compatible");
+
+ const propertyImplicit = Temporal.ZonedDateTime.from(fields, {});
+ assert.sameValue(propertyImplicit.epochNanoseconds, expected, "default disambiguation is compatible");
+
+ const implicit = Temporal.ZonedDateTime.from(fields);
+ assert.sameValue(implicit.epochNanoseconds, expected, "default disambiguation is compatible");
+});
+
+const offsetFields = { year: 2000, month: 5, day: 2, offset: "+23:59", timeZone: "UTC" };
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(offsetFields, undefined), "default offset is reject");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(offsetFields, {}), "default offset is reject");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(offsetFields), "default offset is reject");
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/overflow-invalid-string.js b/test/built-ins/Temporal/ZonedDateTime/from/overflow-invalid-string.js
new file mode 100644
index 00000000000..5913d3e7f96
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/overflow-invalid-string.js
@@ -0,0 +1,37 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-totemporalzoneddatetime steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ j. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ 3. Else,
+ a. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.zoneddatetime.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ ...
+ d. Return ...
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"),
+ { year: 2000, month: 5, day: 2, hour: 12, timeZone: "UTC" },
+ "2001-09-09T01:46:40.987654321+00:00[UTC]",
+];
+validValues.forEach((value) => {
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.from(value, { overflow: "other string" }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/overflow-undefined.js b/test/built-ins/Temporal/ZonedDateTime/from/overflow-undefined.js
new file mode 100644
index 00000000000..0caf0fb4fe1
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/overflow-undefined.js
@@ -0,0 +1,45 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-totemporalzoneddatetime steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ j. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ 3. Else,
+ a. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.zoneddatetime.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ ...
+ d. Return ...
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"),
+ "2001-09-09T01:46:40.987654321+00:00[UTC]",
+];
+validValues.forEach((value) => {
+ const explicit = Temporal.ZonedDateTime.from(value, { overflow: undefined });
+ assert.sameValue(explicit.epochNanoseconds, 1_000_000_000_987_654_321n, "overflow is ignored");
+ const implicit = Temporal.ZonedDateTime.from(value, {});
+ assert.sameValue(implicit.epochNanoseconds, 1_000_000_000_987_654_321n, "overflow is ignored");
+});
+
+const propertyBag = { year: 2000, month: 15, day: 34, hour: 12, timeZone: "UTC" };
+const explicit = Temporal.ZonedDateTime.from(propertyBag, { overflow: undefined });
+assert.sameValue(explicit.epochNanoseconds, 978_264_000_000_000_000n, "default overflow is constrain");
+
+// See options-undefined for {}
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/overflow-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/from/overflow-wrong-type.js
new file mode 100644
index 00000000000..bd1491be460
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/overflow-wrong-type.js
@@ -0,0 +1,64 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-totemporalzoneddatetime steps 2–3:
+ 2. If Type(_item_) is Object, then
+ ...
+ j. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ 3. Else,
+ a. Perform ? ToTemporalOverflow(_options_).
+ sec-temporal.zoneddatetime.from steps 2–3:
+ 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ a. Perform ? ToTemporalOverflow(_options_).
+ ...
+ d. Return ...
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const validValues = [
+ new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"),
+ "2001-09-09T01:46:40.987654321+00:00[UTC]",
+];
+validValues.forEach((value) => TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => Temporal.ZonedDateTime.from(value, { overflow }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_987_654_321n, descr),
+));
+
+// See TemporalHelpers.checkStringOptionWrongType(); this code path has
+// different expectations for observable calls
+const propertyBag = { year: 2001, month: 9, day: 9, hour: 1, minute: 46, second: 40, timeZone: "UTC" };
+
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: null }), "null");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: true }), "true");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: false }), "false");
+assert.throws(TypeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: Symbol() }), "symbol");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: 2n }), "bigint");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: {} }), "plain object");
+
+// toString property is read once by Calendar.dateFromFields() in the builtin
+// calendars, to get the option value for the date part, and then once again
+// internally to get the option value for the time part.
+const expected = [
+ "get overflow.toString",
+ "call overflow.toString",
+ "get overflow.toString",
+ "call overflow.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");
+const result = Temporal.ZonedDateTime.from(propertyBag, { overflow: observer });
+assert.sameValue(result.epochNanoseconds, 1_000_000_000_000_000_000n, "object with toString");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/from/prop-desc.js
new file mode 100644
index 00000000000..3b8bc6c65d8
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: The "from" property of Temporal.ZonedDateTime
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.from,
+ "function",
+ "`typeof ZonedDateTime.from` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime, "from", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/read-time-fields-before-datefromfields.js b/test/built-ins/Temporal/ZonedDateTime/from/read-time-fields-before-datefromfields.js
new file mode 100644
index 00000000000..09c14956d98
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/read-time-fields-before-datefromfields.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.zoneddatetime.from step 3:
+ 3. Return ? ToTemporalDateTime(_item_, _options_).
+ sec-temporal-totemporalzoneddatetime step 2.j:
+ j. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC", calendar });
+
+assert.sameValue(datetime.hour, 12, "hour value");
+assert.sameValue(datetime.minute, 34, "minute value");
+assert.sameValue(datetime.second, 56, "second value");
+assert.sameValue(datetime.millisecond, 987, "millisecond value");
+assert.sameValue(datetime.microsecond, 654, "microsecond value");
+assert.sameValue(datetime.nanosecond, 321, "nanosecond value");
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/subclassing-ignored.js b/test/built-ins/Temporal/ZonedDateTime/from/subclassing-ignored.js
new file mode 100644
index 00000000000..fc4a0193e66
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/subclassing-ignored.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: The receiver is never called when calling from()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnoredStatic(
+ Temporal.ZonedDateTime,
+ "from",
+ ["2000-01-01T00:00:00.00000001+00:00[UTC]"],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 946684800_000_000_010n, "epochNanoseconds result");
+ assert.sameValue(result.year, 2000, "year result");
+ assert.sameValue(result.month, 1, "month result");
+ assert.sameValue(result.day, 1, "day result");
+ assert.sameValue(result.hour, 0, "hour result");
+ assert.sameValue(result.minute, 0, "minute result");
+ assert.sameValue(result.second, 0, "second result");
+ assert.sameValue(result.millisecond, 0, "millisecond result");
+ assert.sameValue(result.microsecond, 0, "microsecond result");
+ assert.sameValue(result.nanosecond, 10, "nanosecond result");
+ },
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/timezone-getpossibleinstantsfor-iterable.js b/test/built-ins/Temporal/ZonedDateTime/from/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 00000000000..de48fe6bd4f
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.zoneddatetime.from step 3:
+ 3. Return ? ToTemporalZonedDateTime(_item_, _options_).
+ sec-temporal-totemporalzoneddatetime step 7:
+ 7. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "2000-05-02T00:00:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone });
+}, expected);
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/timezone-string-datetime.js b/test/built-ins/Temporal/ZonedDateTime/from/timezone-string-datetime.js
new file mode 100644
index 00000000000..60698a06b6f
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/timezone-string-datetime.js
@@ -0,0 +1,42 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone }), "bare date-time string is not a time zone");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone: { timeZone } }), "bare date-time string is not a time zone");
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone });
+assert.sameValue(result1.timeZone.id, "UTC", "date-time + Z is UTC time zone");
+const result2 = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone: { timeZone } });
+assert.sameValue(result2.timeZone.id, "UTC", "date-time + Z is UTC time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result3 = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone });
+assert.sameValue(result3.timeZone.id, "-07:00", "date-time + offset is the offset time zone");
+const result4 = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone: { timeZone } });
+assert.sameValue(result4.timeZone.id, "-07:00", "date-time + offset is the offset time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30[America/Vancouver]";
+const result5 = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone });
+assert.sameValue(result5.timeZone.id, "America/Vancouver", "date-time + IANA annotation is the IANA time zone");
+const result6 = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone: { timeZone } });
+assert.sameValue(result6.timeZone.id, "America/Vancouver", "date-time + IANA annotation is the IANA time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30Z[America/Vancouver]";
+const result7 = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone });
+assert.sameValue(result7.timeZone.id, "America/Vancouver", "date-time + Z + IANA annotation is the IANA time zone");
+const result8 = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone: { timeZone } });
+assert.sameValue(result8.timeZone.id, "America/Vancouver", "date-time + Z + IANA annotation is the IANA time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30-07:00[America/Vancouver]";
+const result9 = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone });
+assert.sameValue(result9.timeZone.id, "America/Vancouver", "date-time + offset + IANA annotation is the IANA time zone");
+const result10 = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone: { timeZone } });
+assert.sameValue(result10.timeZone.id, "America/Vancouver", "date-time + offset + IANA annotation is the IANA time zone (string in property bag)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/from/zoneddatetime-string.js b/test/built-ins/Temporal/ZonedDateTime/from/zoneddatetime-string.js
new file mode 100644
index 00000000000..785bc986e9d
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/from/zoneddatetime-string.js
@@ -0,0 +1,37 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.from
+description: Conversion of ISO date-time strings to Temporal.ZonedDateTime instances
+features: [Temporal]
+---*/
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str), "bare date-time string is not a ZonedDateTime");
+str = "1970-01-01T00:00Z";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str), "date-time + Z is not a ZonedDateTime");
+str = "1970-01-01T00:00+01:00";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str), "date-time + offset is not a ZonedDateTime");
+
+str = "1970-01-01T00:00[Europe/Berlin]";
+const result1 = Temporal.ZonedDateTime.from(str);
+assert.sameValue(result1.epochNanoseconds, -3600_000_000_000n, "date-time + IANA annotation preserves wall time in the time zone");
+assert.sameValue(result1.timeZone.toString(), "Europe/Berlin", "IANA annotation is not ignored");
+
+str = "1970-01-01T00:00Z[Europe/Berlin]";
+const result2 = Temporal.ZonedDateTime.from(str);
+assert.sameValue(result2.epochNanoseconds, 0n, "date-time + Z + IANA annotation preserves exact time in the time zone");
+assert.sameValue(result2.timeZone.toString(), "Europe/Berlin", "IANA annotation is not ignored");
+
+str = "1970-01-01T00:00+01:00[Europe/Berlin]";
+const result3 = Temporal.ZonedDateTime.from(str);
+assert.sameValue(result3.epochNanoseconds, -3600_000_000_000n, "date-time + offset + IANA annotation ensures both exact and wall time match");
+assert.sameValue(result3.timeZone.toString(), "Europe/Berlin", "IANA annotation is not ignored");
+
+str = "1970-01-01T00:00-04:15[Europe/Berlin]";
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str), "date-time + offset + IANA annotation throws if wall time and exact time mismatch");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str, { offset: "reject" }), "date-time + offset + IANA annotation throws if wall time and exact time mismatch (explicit reject option)");
+const result4 = Temporal.ZonedDateTime.from(str, { offset: "ignore" });
+assert.sameValue(result4.epochNanoseconds, -3600_000_000_000n, "date-time + wrong offset + IANA annotation preserves wall time in the time zone (offset: ignore option)");
+assert.sameValue(result4.timeZone.toString(), "Europe/Berlin", "IANA annotation is not ignored");
diff --git a/test/built-ins/Temporal/ZonedDateTime/length.js b/test/built-ins/Temporal/ZonedDateTime/length.js
new file mode 100644
index 00000000000..896826459bd
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime
+description: Temporal.ZonedDateTime.length is 2
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime, "length", {
+ value: 2,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/missing-arguments.js b/test/built-ins/Temporal/ZonedDateTime/missing-arguments.js
new file mode 100644
index 00000000000..1194b625edd
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/missing-arguments.js
@@ -0,0 +1,10 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime
+description: TypeError thrown when constructor invoked with no argument
+features: [Temporal]
+---*/
+
+assert.throws(TypeError, () => new Temporal.ZonedDateTime());
diff --git a/test/built-ins/Temporal/ZonedDateTime/name.js b/test/built-ins/Temporal/ZonedDateTime/name.js
new file mode 100644
index 00000000000..e0dd855c2a1
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime
+description: Temporal.ZonedDateTime.name is "ZonedDateTime"
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime, "name", {
+ value: "ZonedDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prop-desc.js
new file mode 100644
index 00000000000..9d371e3ff13
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime
+description: The "ZonedDateTime" property of Temporal
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime,
+ "function",
+ "`typeof ZonedDateTime` is `function`"
+);
+
+verifyProperty(Temporal, "ZonedDateTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/add/argument-string-negative-fractional-units.js b/test/built-ins/Temporal/ZonedDateTime/prototype/add/argument-string-negative-fractional-units.js
new file mode 100644
index 00000000000..0c62f3b949c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/add/argument-string-negative-fractional-units.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.add
+description: Strings with fractional duration units are treated with the correct sign
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+const resultHours = instance.add("-PT24.567890123H");
+assert.sameValue(resultHours.epochNanoseconds, 999_911_555_595_557_201n, "negative fractional hours");
+
+const resultMinutes = instance.add("-PT1440.567890123M");
+assert.sameValue(resultMinutes.epochNanoseconds, 999_913_565_926_592_621n, "negative fractional minutes");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/add/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/add/builtin.js
new file mode 100644
index 00000000000..bc45c4ebd05
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/add/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.add
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.add
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.add),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.add),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.add),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.add.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/add/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/add/length.js
new file mode 100644
index 00000000000..f3207afb873
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/add/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.add
+description: Temporal.ZonedDateTime.prototype.add.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.add, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/add/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/add/name.js
new file mode 100644
index 00000000000..63db9362bde
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/add/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.add
+description: Temporal.ZonedDateTime.prototype.add.name is "add".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.add, "name", {
+ value: "add",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/add/negative-epochnanoseconds.js b/test/built-ins/Temporal/ZonedDateTime/prototype/add/negative-epochnanoseconds.js
new file mode 100644
index 00000000000..df9ca166f8c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/add/negative-epochnanoseconds.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.add
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const result = datetime.add(new Temporal.Duration(0, 0, 0, 1));
+assert.sameValue(result.epochNanoseconds, -13763364_999_999_999n);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/add/non-integer-throws-rangeerror.js b/test/built-ins/Temporal/ZonedDateTime/prototype/add/non-integer-throws-rangeerror.js
new file mode 100644
index 00000000000..00dd22aea01
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/add/non-integer-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.add
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.add({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.add({ [field]: -1.5 }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/add/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/add/not-a-constructor.js
new file mode 100644
index 00000000000..e19e09c6734
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/add/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.add
+description: >
+ Temporal.ZonedDateTime.prototype.add does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.add();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.add), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.add)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/add/options-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/add/options-undefined.js
new file mode 100644
index 00000000000..db10979fb0c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/add/options-undefined.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.add
+description: Verify that undefined options are handled correctly.
+features: [BigInt, Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(949322096_987_654_321n, "UTC");
+const duration = { months: 1 };
+
+const explicit = datetime.add(duration, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = datetime.add(duration);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-invalid-string.js b/test/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-invalid-string.js
new file mode 100644
index 00000000000..4c88b568784
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-invalid-string.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.add
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-addzoneddatetime step 6:
+ 6. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.zoneddatetime.prototype.add step 7:
+ 7. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZone_, _calendar_, _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const duration = new Temporal.Duration(0, 0, 0, 1);
+assert.throws(RangeError, () => datetime.add(duration, { overflow: "other string" }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-undefined.js
new file mode 100644
index 00000000000..04b6dc0bd3a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-undefined.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.add
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-addzoneddatetime step 6:
+ 6. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.zoneddatetime.prototype.add step 7:
+ 7. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZone_, _calendar_, _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-1n, "UTC");
+const duration = new Temporal.Duration(0, 2);
+
+const explicit = datetime.add(duration, { overflow: undefined });
+assert.sameValue(explicit.epochNanoseconds, 5097599_999_999_999n, "default overflow is constrain");
+const implicit = datetime.add(duration, {});
+assert.sameValue(implicit.epochNanoseconds, 5097599_999_999_999n, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-wrong-type.js
new file mode 100644
index 00000000000..41eb9ad9aa8
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-wrong-type.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.add
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-addzoneddatetime step 6:
+ 6. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.zoneddatetime.prototype.add step 7:
+ 7. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZone_, _calendar_, _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const duration = new Temporal.Duration(0, 0, 0, 1);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => datetime.add(duration, { overflow }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_086_400_987_654_321n, descr),
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/add/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/add/prop-desc.js
new file mode 100644
index 00000000000..507f8f3974a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/add/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.add
+description: The "add" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.add,
+ "function",
+ "`typeof ZonedDateTime.prototype.add` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "add", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/add/subclassing-ignored.js b/test/built-ins/Temporal/ZonedDateTime/prototype/add/subclassing-ignored.js
new file mode 100644
index 00000000000..cd608d8dae3
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/add/subclassing-ignored.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.add
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.ZonedDateTime,
+ [10n, "UTC"],
+ "add",
+ [{ nanoseconds: 5 }],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 15n, "epochNanoseconds result");
+ assert.sameValue(result.year, 1970, "year result");
+ assert.sameValue(result.month, 1, "month result");
+ assert.sameValue(result.day, 1, "day result");
+ assert.sameValue(result.hour, 0, "hour result");
+ assert.sameValue(result.minute, 0, "minute result");
+ assert.sameValue(result.second, 0, "second result");
+ assert.sameValue(result.millisecond, 0, "millisecond result");
+ assert.sameValue(result.microsecond, 0, "microsecond result");
+ assert.sameValue(result.nanosecond, 15, "nanosecond result");
+ },
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..7b1526902bd
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.add
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.add(duration));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..96cf43a7c2d
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.add
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.add(duration));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..bcccc035c23
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.add
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.add(duration));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/calendar/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/calendar/prop-desc.js
new file mode 100644
index 00000000000..323413d3c14
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/calendar/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.calendar
+description: The "calendar" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "calendar");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/day/balance-negative-time-units.js b/test/built-ins/Temporal/ZonedDateTime/prototype/day/balance-negative-time-units.js
new file mode 100644
index 00000000000..a6670be9591
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/day/balance-negative-time-units.js
@@ -0,0 +1,42 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.day
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-get-temporal.zoneddatetime.prototype.day step 6:
+ 6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
+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 = new Temporal.TimeZone("-00:00:00.000000002");
+const datetime = new Temporal.ZonedDateTime(86400_000_000_001n, tz);
+
+assert.sameValue(datetime.day, 1);
+assert.sameValue(datetime.hour, 23);
+assert.sameValue(datetime.minute, 59);
+assert.sameValue(datetime.second, 59);
+assert.sameValue(datetime.millisecond, 999);
+assert.sameValue(datetime.microsecond, 999);
+assert.sameValue(datetime.nanosecond, 999);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/day/calendar-returns-infinity.js b/test/built-ins/Temporal/ZonedDateTime/prototype/day/calendar-returns-infinity.js
new file mode 100644
index 00000000000..d26b459ccbe
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/day/calendar-returns-infinity.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.day
+description: Getter throws if the calendar returns ±∞ from its day method
+features: [Temporal]
+---*/
+
+class InfinityCalendar extends Temporal.Calendar {
+ constructor(positive) {
+ super("iso8601");
+ this.positive = positive;
+ }
+
+ day() {
+ return this.positive ? Infinity : -Infinity;
+ }
+}
+
+const pos = new InfinityCalendar(true);
+const instance1 = new Temporal.ZonedDateTime(0n, "UTC", pos);
+assert.throws(RangeError, () => instance1.day);
+
+const neg = new InfinityCalendar(false);
+const instance2 = new Temporal.ZonedDateTime(0n, "UTC", neg);
+assert.throws(RangeError, () => instance2.day);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/day/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/day/prop-desc.js
new file mode 100644
index 00000000000..ba69d6a73e7
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/day/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.day
+description: The "day" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "day");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..0c895c4dc3b
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.day
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.day);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..37653f356bf
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.day
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.day);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..56625aecedd
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.day
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.day);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/prop-desc.js
new file mode 100644
index 00000000000..999b35a700e
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.dayofweek
+description: The "dayOfWeek" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "dayOfWeek");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..b447f3a213d
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.dayofweek
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.dayOfWeek);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..0fe127c9d67
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.dayofweek
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.dayOfWeek);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..d3260e80dfe
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.dayofweek
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.dayOfWeek);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/prop-desc.js
new file mode 100644
index 00000000000..a8b0e8e7af2
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.dayofyear
+description: The "dayOfYear" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "dayOfYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..026fd7b8cde
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.dayofyear
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.dayOfYear);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..a4a549d34a1
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.dayofyear
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.dayOfYear);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..1a075b12543
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.dayofyear
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.dayOfYear);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/prop-desc.js
new file mode 100644
index 00000000000..933f97fb52b
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.daysinmonth
+description: The "daysInMonth" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "daysInMonth");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..faf1ad8ed55
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.daysinmonth
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.daysInMonth);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..04c178a7582
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.daysinmonth
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.daysInMonth);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..f679b31e057
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.daysinmonth
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.daysInMonth);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/prop-desc.js
new file mode 100644
index 00000000000..d4445a8b1d3
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.daysinweek
+description: The "daysInWeek" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "daysInWeek");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..32c2a85e04e
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.daysinweek
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.daysInWeek);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..761009ad460
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.daysinweek
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.daysInWeek);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..51ba3543268
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.daysinweek
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.daysInWeek);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/prop-desc.js
new file mode 100644
index 00000000000..b8c5211a4f6
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.daysinyear
+description: The "daysInYear" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "daysInYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..5310ae0bc56
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.daysinyear
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.daysInYear);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..8257af59e56
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.daysinyear
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.daysInYear);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..d60ff6634bd
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.daysinyear
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.daysInYear);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/basic.js b/test/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/basic.js
new file mode 100644
index 00000000000..2c31cef34a2
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/basic.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.epochmicroseconds
+description: Basic tests for epochMicroseconds.
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = new Temporal.ZonedDateTime(217175010_123_456_789n, "UTC");
+assert.sameValue(afterEpoch.epochMicroseconds, 217175010_123_456n, "epochMicroseconds post epoch");
+assert.sameValue(typeof afterEpoch.epochMicroseconds, "bigint", "epochMicroseconds value is a bigint");
+
+const beforeEpoch = new Temporal.ZonedDateTime(-217175010_876_543_211n, "UTC");
+assert.sameValue(beforeEpoch.epochMicroseconds, -217175010_876_543n, "epochMicroseconds pre epoch");
+assert.sameValue(typeof beforeEpoch.epochMicroseconds, "bigint", "epochMicroseconds value is a bigint");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/prop-desc.js
new file mode 100644
index 00000000000..2ad80412836
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.epochmicroseconds
+description: The "epochMicroseconds" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "epochMicroseconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/basic.js b/test/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/basic.js
new file mode 100644
index 00000000000..2208a5cb8ce
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/basic.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.epochmilliseconds
+description: Basic tests for epochMilliseconds.
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = new Temporal.ZonedDateTime(217175010_123_456_789n, "UTC");
+assert.sameValue(afterEpoch.epochMilliseconds, 217175010_123, "epochMilliseconds post epoch");
+assert.sameValue(typeof afterEpoch.epochMilliseconds, "number", "epochMilliseconds value is a number");
+
+const beforeEpoch = new Temporal.ZonedDateTime(-217175010_876_543_211n, "UTC");
+assert.sameValue(beforeEpoch.epochMilliseconds, -217175010_876, "epochMilliseconds pre epoch");
+assert.sameValue(typeof beforeEpoch.epochMilliseconds, "number", "epochMilliseconds value is a number");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/prop-desc.js
new file mode 100644
index 00000000000..68cd528f315
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.epochmilliseconds
+description: The "epochMilliseconds" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "epochMilliseconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/basic.js b/test/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/basic.js
new file mode 100644
index 00000000000..cc5cf2c32f8
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/basic.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.epochnanoseconds
+description: Basic tests for epochNanoseconds.
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = new Temporal.ZonedDateTime(217175010_123_456_789n, "UTC");
+assert.sameValue(afterEpoch.epochNanoseconds, 217175010_123_456_789n, "epochNanoseconds post epoch");
+assert.sameValue(typeof afterEpoch.epochNanoseconds, "bigint", "epochNanoseconds value is a bigint");
+
+const beforeEpoch = new Temporal.ZonedDateTime(-217175010_876_543_211n, "UTC");
+assert.sameValue(beforeEpoch.epochNanoseconds, -217175010_876_543_211n, "epochNanoseconds pre epoch");
+assert.sameValue(typeof beforeEpoch.epochNanoseconds, "bigint", "epochNanoseconds value is a bigint");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/prop-desc.js
new file mode 100644
index 00000000000..83d2e49da55
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.epochnanoseconds
+description: The "epochNanoseconds" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "epochNanoseconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/basic.js b/test/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/basic.js
new file mode 100644
index 00000000000..651c254f89d
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/basic.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.epochseconds
+description: Basic tests for epochSeconds.
+features: [BigInt, Temporal]
+---*/
+
+const afterEpoch = new Temporal.ZonedDateTime(217175010_123_456_789n, "UTC");
+assert.sameValue(afterEpoch.epochSeconds, 217175010, "epochSeconds post epoch");
+assert.sameValue(typeof afterEpoch.epochSeconds, "number", "epochSeconds value is a number");
+
+const beforeEpoch = new Temporal.ZonedDateTime(-217175010_876_543_211n, "UTC");
+assert.sameValue(beforeEpoch.epochSeconds, -217175010, "epochSeconds pre epoch");
+assert.sameValue(typeof beforeEpoch.epochSeconds, "number", "epochSeconds value is a number");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/prop-desc.js
new file mode 100644
index 00000000000..28749fb60d1
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.epochseconds
+description: The "epochSeconds" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "epochSeconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..eb0081e6f21
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.equals
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ assert.throws(RangeError, () => datetime.equals({ year: 2000, month: 5, day: 2, hour: 12, timeZone }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..0b6064067cd
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.equals
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ assert.throws(RangeError, () => datetime.equals({ year: 2000, month: 5, day: 2, hour: 12, timeZone }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..7a57cce8242
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.equals
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ assert.throws(TypeError, () => datetime.equals({ year: 2000, month: 5, day: 2, hour: 12, timeZone }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin.js
new file mode 100644
index 00000000000..8743669efa8
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.equals
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.equals
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.equals),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.equals),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.equals),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.equals.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-fields-iterable.js b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-fields-iterable.js
new file mode 100644
index 00000000000..9efae1eece1
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-fields-iterable.js
@@ -0,0 +1,39 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.equals
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.zoneddatetime.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "hour",
+ "microsecond",
+ "millisecond",
+ "minute",
+ "month",
+ "monthCode",
+ "nanosecond",
+ "second",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+datetime.equals({ year: 2005, month: 6, day: 2, timeZone: "UTC", calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-temporal-object.js b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-temporal-object.js
new file mode 100644
index 00000000000..237be81107b
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.equals
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.zoneddatetime.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+ datetime.equals({ year: 2005, month: 6, day: 2, timeZone: "UTC", calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/equals/infinity-throws-rangeerror.js b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..f25d12085d1
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.zoneddatetime.prototype.equals
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.equals({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/equals/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/length.js
new file mode 100644
index 00000000000..c0794a3a999
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.equals
+description: Temporal.ZonedDateTime.prototype.equals.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.equals, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/equals/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/name.js
new file mode 100644
index 00000000000..4b407a924c7
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.equals
+description: Temporal.ZonedDateTime.prototype.equals.name is "equals".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.equals, "name", {
+ value: "equals",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/equals/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/not-a-constructor.js
new file mode 100644
index 00000000000..91aab39fdd3
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.equals
+description: >
+ Temporal.ZonedDateTime.prototype.equals does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.equals();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.equals), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.equals)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/equals/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/prop-desc.js
new file mode 100644
index 00000000000..0631c6a45bc
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.equals
+description: The "equals" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.equals,
+ "function",
+ "`typeof ZonedDateTime.prototype.equals` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "equals", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/equals/read-time-fields-before-datefromfields.js b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/read-time-fields-before-datefromfields.js
new file mode 100644
index 00000000000..c5d5bd9cdec
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/read-time-fields-before-datefromfields.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.equals
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.zoneddatetime.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 2.j:
+ j. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+const result = datetime.equals({ year: 2001, month: 9, day: 9, hour: 1, minute: 46, second: 40, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC", calendar });
+
+assert(result, "time fields are not modified");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/equals/timezone-getpossibleinstantsfor-iterable.js b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 00000000000..4a0cc24f772
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,38 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.equals
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.zoneddatetime.prototype.equals step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 7:
+ 7. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Not called on the instance's time zone
+
+const expected1 = [];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone);
+ datetime.equals({ year: 2005, month: 6, day: 2, timeZone: "UTC" });
+}, expected1);
+
+// Called on the argument's time zone
+
+const expected2 = [
+ "2005-06-02T00:00:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+ datetime.equals({ year: 2005, month: 6, day: 2, timeZone });
+}, expected2);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/equals/timezone-string-datetime.js b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/timezone-string-datetime.js
new file mode 100644
index 00000000000..82a642b71a5
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/timezone-string-datetime.js
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.equals
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let expectedTimeZone = "UTC";
+const instance1 = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+
+let timeZone = "2021-02-19T17:30";
+assert.throws(RangeError, () => instance1.equals({ year: 1970, month: 1, day: 1, timeZone }), "bare date-time string is not a time zone");
+assert.throws(RangeError, () => instance1.equals({ year: 1970, month: 1, day: 1, timeZone: { timeZone } }), "bare date-time string is not a time zone");
+
+// The following are all valid strings so should not throw. They should produce
+// expectedTimeZone, so additionally the operation should return true, because
+// the property bag will produce an instance that's equal to the receiver.
+
+timeZone = "2021-02-19T17:30Z";
+assert(instance1.equals({ year: 1970, month: 1, day: 1, timeZone }), "date-time + Z is UTC time zone");
+assert(instance1.equals({ year: 1970, month: 1, day: 1, timeZone: { timeZone } }), "date-time + Z is UTC time zone (string in property bag)");
+
+expectedTimeZone = "-08:00";
+const instance2 = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+timeZone = "2021-02-19T17:30-08:00";
+assert(instance2.equals({ year: 1969, month: 12, day: 31, hour: 16, timeZone }), "date-time + offset is the offset time zone");
+assert(instance2.equals({ year: 1969, month: 12, day: 31, hour: 16, timeZone: { timeZone } }), "date-time + offset is the offset time zone (string in property bag)");
+
+expectedTimeZone = "America/Vancouver";
+const instance3 = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+timeZone = "2021-02-19T17:30[America/Vancouver]";
+assert(instance3.equals({ year: 1969, month: 12, day: 31, hour: 16, timeZone }), "date-time + IANA annotation is the IANA time zone");
+assert(instance3.equals({ year: 1969, month: 12, day: 31, hour: 16, timeZone: { timeZone } }), "date-time + IANA annotation is the IANA time zone (string in property bag)");
+
+timeZone = "2021-02-19T17:30Z[America/Vancouver]";
+assert(instance3.equals({ year: 1969, month: 12, day: 31, hour: 16, timeZone }), "date-time + Z + IANA annotation is the IANA time zone");
+assert(instance3.equals({ year: 1969, month: 12, day: 31, hour: 16, timeZone: { timeZone } }), "date-time + Z + IANA annotation is the IANA time zone (string in property bag)");
+
+timeZone = "2021-02-19T17:30-08:00[America/Vancouver]";
+assert(instance3.equals({ year: 1969, month: 12, day: 31, hour: 16, timeZone }), "date-time + offset + IANA annotation is the IANA time zone");
+assert(instance3.equals({ year: 1969, month: 12, day: 31, hour: 16, timeZone: { timeZone } }), "date-time + offset + IANA annotation is the IANA time zone (string in property bag)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/equals/zoneddatetime-string.js b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/zoneddatetime-string.js
new file mode 100644
index 00000000000..0253dae7696
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/zoneddatetime-string.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.equals
+description: Conversion of ISO date-time strings to Temporal.ZonedDateTime instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, "Europe/Berlin");
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.equals(str), "bare date-time string is not a ZonedDateTime");
+str = "1970-01-01T00:00Z";
+assert.throws(RangeError, () => instance.equals(str), "date-time + Z is not a ZonedDateTime");
+str = "1970-01-01T00:00+01:00";
+assert.throws(RangeError, () => instance.equals(str), "date-time + offset is not a ZonedDateTime");
+
+str = "1970-01-01T00:00[Europe/Berlin]";
+const result1 = instance.equals(str);
+assert.sameValue(result1, false, "date-time + IANA annotation preserves wall time in the time zone");
+
+str = "1970-01-01T00:00Z[Europe/Berlin]";
+const result2 = instance.equals(str);
+assert.sameValue(result2, true, "date-time + Z + IANA annotation preserves exact time in the time zone");
+
+str = "1970-01-01T00:00+01:00[Europe/Berlin]";
+const result3 = instance.equals(str);
+assert.sameValue(result3, false, "date-time + offset + IANA annotation ensures both exact and wall time match");
+
+str = "1970-01-01T00:00-04:15[Europe/Berlin]";
+assert.throws(RangeError, () => instance.equals(str), "date-time + offset + IANA annotation throws if wall time and exact time mismatch");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/balance-negative-time-units.js b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/balance-negative-time-units.js
new file mode 100644
index 00000000000..e8557b7c917
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/balance-negative-time-units.js
@@ -0,0 +1,39 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.getisofields
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-get-temporal.zoneddatetime.prototype.getisofields step 7:
+ 7. Let _dateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// ZonedDateTime
+const tz = new Temporal.TimeZone("-00:00:00.000000002");
+const datetime = new Temporal.ZonedDateTime(1001n, tz);
+
+const fields = datetime.getISOFields();
+
+assert.sameValue(fields.isoMicrosecond, 0);
+assert.sameValue(fields.isoNanosecond, 999);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/builtin.js
new file mode 100644
index 00000000000..ed925e1e4ed
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.getisofields
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.getISOFields
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.getISOFields),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.getISOFields),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.getISOFields),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.getISOFields.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-names.js b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-names.js
new file mode 100644
index 00000000000..150e8dd11d9
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-names.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.getisofields
+description: Correct field names on the object returned from getISOFields
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_086_400_987_654_321n, "UTC");
+
+const result = datetime.getISOFields();
+assert.sameValue(result.isoYear, 2001, "isoYear result");
+assert.sameValue(result.isoMonth, 9, "isoMonth result");
+assert.sameValue(result.isoDay, 10, "isoDay result");
+assert.sameValue(result.isoHour, 1, "isoHour result");
+assert.sameValue(result.isoMinute, 46, "isoMinute result");
+assert.sameValue(result.isoSecond, 40, "isoSecond result");
+assert.sameValue(result.isoMillisecond, 987, "isoMillisecond result");
+assert.sameValue(result.isoMicrosecond, 654, "isoMicrosecond result");
+assert.sameValue(result.isoNanosecond, 321, "isoNanosecond result");
+assert.sameValue(result.offset, "+00:00", "offset result");
+assert.sameValue(result.calendar.id, "iso8601", "calendar result");
+assert.sameValue(result.timeZone.id, "UTC", "timeZone result");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-prop-desc.js
new file mode 100644
index 00000000000..30afae90935
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-prop-desc.js
@@ -0,0 +1,35 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.getisofields
+description: Properties on the returned object have the correct descriptor
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoHour",
+ "isoMicrosecond",
+ "isoMillisecond",
+ "isoMinute",
+ "isoMonth",
+ "isoNanosecond",
+ "isoSecond",
+ "isoYear",
+ "offset",
+ "timeZone",
+];
+
+const datetime = new Temporal.ZonedDateTime(1_000_086_400_987_654_321n, "UTC");
+const result = datetime.getISOFields();
+
+for (const property of expected) {
+ verifyProperty(result, property, {
+ writable: true,
+ enumerable: true,
+ configurable: true,
+ });
+}
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-traversal-order.js b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-traversal-order.js
new file mode 100644
index 00000000000..a7b600d1187
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-traversal-order.js
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.getisofields
+description: Properties added in correct order to object returned from getISOFields
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "calendar",
+ "isoDay",
+ "isoHour",
+ "isoMicrosecond",
+ "isoMillisecond",
+ "isoMinute",
+ "isoMonth",
+ "isoNanosecond",
+ "isoSecond",
+ "isoYear",
+ "offset",
+ "timeZone",
+];
+
+const datetime = new Temporal.ZonedDateTime(1_000_086_400_987_654_321n, "UTC");
+const result = datetime.getISOFields();
+
+assert.compareArray(Object.keys(result), expected);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/length.js
new file mode 100644
index 00000000000..0a99d6dcc5c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.getisofields
+description: Temporal.ZonedDateTime.prototype.getISOFields.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.getISOFields, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/name.js
new file mode 100644
index 00000000000..e68c8bdc4e9
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.getisofields
+description: Temporal.ZonedDateTime.prototype.getISOFields.name is "getISOFields".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.getISOFields, "name", {
+ value: "getISOFields",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/negative-epochnanoseconds.js b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/negative-epochnanoseconds.js
new file mode 100644
index 00000000000..7a4350cecc5
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/negative-epochnanoseconds.js
@@ -0,0 +1,37 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.getisofields
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [deepEqual.js]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const calendar = new Temporal.Calendar("iso8601");
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, timeZone, calendar);
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const result = datetime.getISOFields();
+assert.deepEqual(result, {
+ calendar,
+ isoDay: 24,
+ isoHour: 16,
+ isoMicrosecond: 0,
+ isoMillisecond: 0,
+ isoMinute: 50,
+ isoMonth: 7,
+ isoNanosecond: 1,
+ isoSecond: 35,
+ isoYear: 1969,
+ offset: "+00:00",
+ timeZone,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/not-a-constructor.js
new file mode 100644
index 00000000000..7ebdadf8a56
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.getisofields
+description: >
+ Temporal.ZonedDateTime.prototype.getISOFields does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.getISOFields();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.getISOFields), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.getISOFields)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/prop-desc.js
new file mode 100644
index 00000000000..4a7416a6583
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.getisofields
+description: The "getISOFields" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.getISOFields,
+ "function",
+ "`typeof ZonedDateTime.prototype.getISOFields` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "getISOFields", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..ef507e52ada
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.getisofields
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.getISOFields());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..fad48ca9444
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.getisofields
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.getISOFields());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..6ca3f881fbf
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.getisofields
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.getISOFields());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/hour/balance-negative-time-units.js b/test/built-ins/Temporal/ZonedDateTime/prototype/hour/balance-negative-time-units.js
new file mode 100644
index 00000000000..3e229c0a32e
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/hour/balance-negative-time-units.js
@@ -0,0 +1,39 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.hour
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–12:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-get-temporal.zoneddatetime.prototype.hour step 6:
+ 6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
+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 = new Temporal.TimeZone("-00:00:00.000000002");
+const datetime = new Temporal.ZonedDateTime(3600_000_000_001n, tz);
+
+assert.sameValue(datetime.hour, 0);
+assert.sameValue(datetime.minute, 59);
+assert.sameValue(datetime.second, 59);
+assert.sameValue(datetime.millisecond, 999);
+assert.sameValue(datetime.microsecond, 999);
+assert.sameValue(datetime.nanosecond, 999);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/hour/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/hour/prop-desc.js
new file mode 100644
index 00000000000..0e862cbac65
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/hour/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.hour
+description: The "hour" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "hour");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..2580dd770d4
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.hour
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.hour);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..e030ca27f10
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.hour
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.hour);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..75eb9fbaab4
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.hour
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.hour);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/prop-desc.js
new file mode 100644
index 00000000000..43c104349ed
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.hoursinday
+description: The "hoursInDay" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "hoursInDay");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..8b90e4062fa
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.hoursinday
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.hoursInDay);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..20a2e42eb02
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.hoursinday
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.hoursInDay);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..23c89aa7953
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.hoursinday
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.hoursInDay);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getpossibleinstantsfor-iterable.js b/test/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 00000000000..49bcb52c99a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,45 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.hoursinday
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-get-temporal.zoneddatetime.prototype.hoursinday steps 13–14:
+ 13. Let _todayInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _today_, *"compatible"*).
+ 14. Let _tomorrowInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _tomorrow_, *"compatible"*).
+ sec-temporal-builtintimezonegetinstantfor step 1:
+ 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-builtintimezonegetinstantfor step 14:
+ 14. Assert: _disambiguation_ is *"compatible"* or *"later"*.
+ sec-temporal-builtintimezonegetinstantfor step 16:
+ 16. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _later_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected1 = [
+ "2001-09-09T00:00:00",
+ "2001-09-10T00:00:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone);
+ datetime.hoursInDay;
+}, expected1);
+
+// Same, but test the other path where the time doesn't exist and
+// GetPossibleInstantsFor is called again on a later time
+
+const expected2 = [
+ "2030-01-01T00:00:00",
+ "2030-01-01T01:00:00",
+ "2030-01-02T00:00:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.ZonedDateTime(1_893_457_800_000_000_000n, timeZone);
+ datetime.hoursInDay;
+}, expected2);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/prop-desc.js
new file mode 100644
index 00000000000..fcfbe214414
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.inleapyear
+description: The "inLeapYear" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "inLeapYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..67e5b10c697
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.inleapyear
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.inLeapYear);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..7989edce269
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.inleapyear
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.inLeapYear);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..d7e66fcc70e
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.inleapyear
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.inLeapYear);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/microsecond/balance-negative-time-units.js b/test/built-ins/Temporal/ZonedDateTime/prototype/microsecond/balance-negative-time-units.js
new file mode 100644
index 00000000000..f0e5cae171d
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/microsecond/balance-negative-time-units.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.microsecond
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–4:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-get-temporal.zoneddatetime.prototype.microsecond step 6:
+ 6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
+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 = new Temporal.TimeZone("-00:00:00.000000002");
+const datetime = new Temporal.ZonedDateTime(1001n, tz);
+
+assert.sameValue(datetime.microsecond, 0);
+assert.sameValue(datetime.nanosecond, 999);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/microsecond/negative-epochnanoseconds.js b/test/built-ins/Temporal/ZonedDateTime/prototype/microsecond/negative-epochnanoseconds.js
new file mode 100644
index 00000000000..481278a09fc
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/microsecond/negative-epochnanoseconds.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.microsecond
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+assert.sameValue(datetime.microsecond, 0);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/microsecond/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/microsecond/prop-desc.js
new file mode 100644
index 00000000000..fc351e73eab
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/microsecond/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.microsecond
+description: The "microsecond" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "microsecond");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..e56d8609cad
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.microsecond
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.microsecond);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..3c60b801a27
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.microsecond
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.microsecond);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..c9acf66eccc
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.microsecond
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.microsecond);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/millisecond/balance-negative-time-units.js b/test/built-ins/Temporal/ZonedDateTime/prototype/millisecond/balance-negative-time-units.js
new file mode 100644
index 00000000000..7156d9ebdb1
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/millisecond/balance-negative-time-units.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.millisecond
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–6:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-get-temporal.zoneddatetime.prototype.millisecond step 6:
+ 6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
+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 = new Temporal.TimeZone("-00:00:00.000000002");
+const datetime = new Temporal.ZonedDateTime(1_000_001n, tz);
+
+assert.sameValue(datetime.millisecond, 0);
+assert.sameValue(datetime.microsecond, 999);
+assert.sameValue(datetime.nanosecond, 999);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/millisecond/negative-epochnanoseconds.js b/test/built-ins/Temporal/ZonedDateTime/prototype/millisecond/negative-epochnanoseconds.js
new file mode 100644
index 00000000000..bf9bcadde02
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/millisecond/negative-epochnanoseconds.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.millisecond
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+assert.sameValue(datetime.millisecond, 0);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/millisecond/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/millisecond/prop-desc.js
new file mode 100644
index 00000000000..5afdbcb89ee
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/millisecond/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.millisecond
+description: The "millisecond" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "millisecond");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..0b15aad2c8f
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.millisecond
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.millisecond);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..9146d54b0ac
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.millisecond
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.millisecond);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..8b66d91b40e
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.millisecond
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.millisecond);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/minute/balance-negative-time-units.js b/test/built-ins/Temporal/ZonedDateTime/prototype/minute/balance-negative-time-units.js
new file mode 100644
index 00000000000..707ec034037
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/minute/balance-negative-time-units.js
@@ -0,0 +1,38 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.minute
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–10:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-get-temporal.zoneddatetime.prototype.minute step 6:
+ 6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
+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 = new Temporal.TimeZone("-00:00:00.000000002");
+const datetime = new Temporal.ZonedDateTime(60_000_000_001n, tz);
+
+assert.sameValue(datetime.minute, 0);
+assert.sameValue(datetime.second, 59);
+assert.sameValue(datetime.millisecond, 999);
+assert.sameValue(datetime.microsecond, 999);
+assert.sameValue(datetime.nanosecond, 999);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/minute/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/minute/prop-desc.js
new file mode 100644
index 00000000000..b114facb57e
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/minute/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.minute
+description: The "minute" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "minute");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..924e3d95e5a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.minute
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.minute);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..c1576845c2c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.minute
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.minute);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..deec12ba684
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.minute
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.minute);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/month/calendar-returns-infinity.js b/test/built-ins/Temporal/ZonedDateTime/prototype/month/calendar-returns-infinity.js
new file mode 100644
index 00000000000..aa4341dea33
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/month/calendar-returns-infinity.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.month
+description: Getter throws if the calendar returns ±∞ from its month method
+features: [Temporal]
+---*/
+
+class InfinityCalendar extends Temporal.Calendar {
+ constructor(positive) {
+ super("iso8601");
+ this.positive = positive;
+ }
+
+ month() {
+ return this.positive ? Infinity : -Infinity;
+ }
+}
+
+const pos = new InfinityCalendar(true);
+const instance1 = new Temporal.ZonedDateTime(0n, "UTC", pos);
+assert.throws(RangeError, () => instance1.month);
+
+const neg = new InfinityCalendar(false);
+const instance2 = new Temporal.ZonedDateTime(0n, "UTC", neg);
+assert.throws(RangeError, () => instance2.month);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/month/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/month/prop-desc.js
new file mode 100644
index 00000000000..7662d80bac5
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/month/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.month
+description: The "month" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "month");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..aa924d43e09
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.month
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.month);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..95e8c9e7715
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.month
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.month);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..53619e29477
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.month
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.month);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/monthCode/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/monthCode/prop-desc.js
new file mode 100644
index 00000000000..f96c52ce5a3
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/monthCode/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.monthcode
+description: The "monthCode" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "monthCode");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..e1ac5491cc7
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.monthcode
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.monthCode);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..0e7ac82489d
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.monthcode
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.monthCode);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..44ba555abc0
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.monthcode
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.monthCode);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/prop-desc.js
new file mode 100644
index 00000000000..534500eb27c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.monthsinyear
+description: The "monthsInYear" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "monthsInYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..2a39282f902
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.monthsinyear
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.monthsInYear);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..1429ab6929d
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.monthsinyear
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.monthsInYear);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..775e3ff036a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.monthsinyear
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.monthsInYear);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/negative-epochnanoseconds.js b/test/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/negative-epochnanoseconds.js
new file mode 100644
index 00000000000..07ef5f59cdb
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/negative-epochnanoseconds.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.nanosecond
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+assert.sameValue(datetime.nanosecond, 1);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/prop-desc.js
new file mode 100644
index 00000000000..527a522cd9a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.nanosecond
+description: The "nanosecond" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "nanosecond");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..2963371913d
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.nanosecond
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.nanosecond);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..237dd1e9928
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.nanosecond
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.nanosecond);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..5ef67518a73
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.nanosecond
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.nanosecond);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/offset/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/offset/prop-desc.js
new file mode 100644
index 00000000000..cdeba6f0a34
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/offset/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.offset
+description: The "offset" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "offset");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..04ffbc671f7
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.offset
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.offset);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..aa7eec073b2
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.offset
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.offset);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..ab0f673797e
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.offset
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.offset);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/prop-desc.js
new file mode 100644
index 00000000000..9677e533ee7
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.offsetnanoseconds
+description: The "offsetNanoseconds" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "offsetNanoseconds");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..3d9ae8909a4
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.offsetnanoseconds
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.offsetNanoseconds);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..bb48eb69abb
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.offsetnanoseconds
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.offsetNanoseconds);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..cbdc863445c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.offsetnanoseconds
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.offsetNanoseconds);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/builtin.js
new file mode 100644
index 00000000000..99e805a69fa
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.round
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.round),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.round),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.round),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.round.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/calendar-dateadd-called-with-options-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/calendar-dateadd-called-with-options-undefined.js
new file mode 100644
index 00000000000..d660edfe4dd
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/calendar-dateadd-called-with-options-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: >
+ BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the
+ options value
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddUndefinedOptions();
+const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9);
+const instance = new Temporal.ZonedDateTime(7200_000_000_000n, timeZone, calendar);
+instance.round({ smallestUnit: "day" });
+assert.sameValue(calendar.dateAddCallCount, 1);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/dateadd-options.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/dateadd-options.js
new file mode 100644
index 00000000000..6c4cf252a44
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/dateadd-options.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: dateAdd() is called with the correct three arguments
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+let actual = [];
+class Calendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(...args) {
+ actual.push(this, ...args);
+ return super.dateAdd(...args);
+ }
+}
+
+const calendar = new Calendar();
+const zdt = new Temporal.ZonedDateTime(0n, "UTC", calendar);
+const result = zdt.round({ smallestUnit: "day" });
+assert.sameValue(result.epochNanoseconds, 0n, "Result");
+
+assert.sameValue(actual.length, 4, "three arguments");
+assert.sameValue(actual[0], calendar, "this value");
+TemporalHelpers.assertPlainDate(actual[1], 1970, 1, "M01", 1, "date argument");
+TemporalHelpers.assertDuration(actual[2], 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, "duration argument");
+assert.sameValue(actual[3], undefined, "options should be undefined");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/div-zero.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/div-zero.js
new file mode 100644
index 00000000000..ca2e5cdb18b
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/div-zero.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: RangeError thrown if the calculated day length is zero
+features: [Temporal]
+---*/
+
+class Calendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ dateAdd(d) {
+ return d;
+ }
+}
+
+const zdt = new Temporal.ZonedDateTime(0n, "UTC", new Calendar());
+
+const units = ["day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"];
+for (const smallestUnit of units) {
+ assert.throws(RangeError, () => zdt.round({ smallestUnit }));
+}
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/length.js
new file mode 100644
index 00000000000..eddfb2897ff
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: Temporal.ZonedDateTime.prototype.round.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.round, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/name.js
new file mode 100644
index 00000000000..6651d9afd61
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: Temporal.ZonedDateTime.prototype.round.name is "round".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.round, "name", {
+ value: "round",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/negative-epochnanoseconds.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/negative-epochnanoseconds.js
new file mode 100644
index 00000000000..6dedb932719
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/negative-epochnanoseconds.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const result = datetime.round({ smallestUnit: "millisecond" });
+assert.sameValue(result.epochNanoseconds, -13849765_000_000_000n);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/not-a-constructor.js
new file mode 100644
index 00000000000..bee48e8417f
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: >
+ Temporal.ZonedDateTime.prototype.round does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.round();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.round), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.round)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/prop-desc.js
new file mode 100644
index 00000000000..cdd317ec381
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: The "round" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.round,
+ "function",
+ "`typeof ZonedDateTime.prototype.round` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "round", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-nan.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-nan.js
new file mode 100644
index 00000000000..586c4f11ea5
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-nan.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal-totemporaldatetimeroundingincrement step 5:
+ 5. Return ? ToTemporalRoundingIncrement(_normalizedOptions_, _maximum_, *false*).
+ sec-temporal.zoneddatetime.prototype.round step 8:
+ 8. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_options_, _smallestUnit_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+assert.throws(RangeError, () => datetime.round({ smallestUnit: 'second', roundingIncrement: NaN }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-non-integer.js
new file mode 100644
index 00000000000..4bf9d4f4173
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: Rounding for roundingIncrement option
+info: |
+ sec-temporal-totemporalroundingincrement step 7:
+ 7. Set _increment_ to floor(ℝ(_increment_)).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_005n, "UTC");
+const result = datetime.round({ smallestUnit: "nanosecond", roundingIncrement: 2.5 });
+assert.sameValue(result.epochNanoseconds, 1_000_000_000_000_000_006n, "roundingIncrement 2.5 floors to 2");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-out-of-range.js
new file mode 100644
index 00000000000..61763f2f09a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-out-of-range.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ sec-temporal-totemporalroundingincrement step 6:
+ 6. If _increment_ < 1 or _increment_ > _maximum_, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_005n, "UTC");
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: -1 }));
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: 0 }));
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: Infinity }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-undefined.js
new file mode 100644
index 00000000000..152a58121b1
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal-totemporaldatetimeroundingincrement step 5:
+ 5. Return ? ToTemporalRoundingIncrement(_normalizedOptions_, _maximum_, *false*).
+ sec-temporal.zoneddatetime.prototype.round step 8:
+ 8. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_options_, _smallestUnit_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+
+const explicit = datetime.round({ smallestUnit: 'second', roundingIncrement: undefined });
+assert.sameValue(explicit.epochNanoseconds, 1_000_000_001_000_000_000n, "default roundingIncrement is 1");
+
+const implicit = datetime.round({ smallestUnit: 'second' });
+assert.sameValue(implicit.epochNanoseconds, 1_000_000_001_000_000_000n, "default roundingIncrement is 1");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-wrong-type.js
new file mode 100644
index 00000000000..5a01f297a5c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-wrong-type.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal-totemporaldatetimeroundingincrement step 5:
+ 5. Return ? ToTemporalRoundingIncrement(_normalizedOptions_, _maximum_, *false*).
+ sec-temporal.zoneddatetime.prototype.round step 8:
+ 8. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_options_, _smallestUnit_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => datetime.round({ smallestUnit: 'second', roundingIncrement }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_001_000_000_000n, descr),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_000_000_000n, descr),
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-invalid-string.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-invalid-string.js
new file mode 100644
index 00000000000..145e3f615a7
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-invalid-string.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "microsecond", roundingMode: "other string" }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-undefined.js
new file mode 100644
index 00000000000..79b521bd74a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: Fallback value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+
+const explicit1 = datetime.round({ smallestUnit: "microsecond", roundingMode: undefined });
+assert.sameValue(explicit1.epochNanoseconds, 1_000_000_000_123_988_000n, "default roundingMode is halfExpand");
+const implicit1 = datetime.round({ smallestUnit: "microsecond" });
+assert.sameValue(implicit1.epochNanoseconds, 1_000_000_000_123_988_000n, "default roundingMode is halfExpand");
+
+const explicit2 = datetime.round({ smallestUnit: "millisecond", roundingMode: undefined });
+assert.sameValue(explicit2.epochNanoseconds, 1_000_000_000_124_000_000n, "default roundingMode is halfExpand");
+const implicit2 = datetime.round({ smallestUnit: "millisecond" });
+assert.sameValue(implicit2.epochNanoseconds, 1_000_000_000_124_000_000n, "default roundingMode is halfExpand");
+
+const explicit3 = datetime.round({ smallestUnit: "second", roundingMode: undefined });
+assert.sameValue(explicit3.epochNanoseconds, 1_000_000_000_000_000_000n, "default roundingMode is halfExpand");
+const implicit3 = datetime.round({ smallestUnit: "second" });
+assert.sameValue(implicit3.epochNanoseconds, 1_000_000_000_000_000_000n, "default roundingMode is halfExpand");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-wrong-type.js
new file mode 100644
index 00000000000..4d766e3238a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-wrong-type.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "halfExpand",
+ (roundingMode) => datetime.round({ smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_123_988_000n, descr),
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-invalid-string.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-invalid-string.js
new file mode 100644
index 00000000000..bee6892ba97
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-invalid-string.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+assert.throws(RangeError, () => datetime.round({ smallestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-plurals-accepted.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-plurals-accepted.js
new file mode 100644
index 00000000000..2ffe1ec6767
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-plurals-accepted.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const validUnits = [
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => datetime.round({ smallestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-wrong-type.js
new file mode 100644
index 00000000000..5bfa502f1dc
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-wrong-type.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => datetime.round({ smallestUnit }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_123_988_000n, descr),
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/subclassing-ignored.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/subclassing-ignored.js
new file mode 100644
index 00000000000..8bd312e1dee
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/subclassing-ignored.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatime.prototype.round
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.ZonedDateTime,
+ [10n, "UTC"],
+ "round",
+ [{ smallestUnit: 'second', roundingMode: 'ceil' }],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 1_000_000_000n, "epochNanoseconds result");
+ assert.sameValue(result.year, 1970, "year result");
+ assert.sameValue(result.month, 1, "month result");
+ assert.sameValue(result.day, 1, "day result");
+ assert.sameValue(result.hour, 0, "hour result");
+ assert.sameValue(result.minute, 0, "minute result");
+ assert.sameValue(result.second, 1, "second result");
+ assert.sameValue(result.millisecond, 0, "millisecond result");
+ assert.sameValue(result.microsecond, 0, "microsecond result");
+ assert.sameValue(result.nanosecond, 0, "nanosecond result");
+ },
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..e2560dcfc2c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.round({ smallestUnit: "second" }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..425274191fa
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.round({ smallestUnit: "second" }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..e60edea3a5b
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.round({ smallestUnit: "second" }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getpossibleinstantsfor-iterable.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 00000000000..b76a3b08a5b
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.round
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.zoneddatetime.prototype.round steps 14, 16, and 20:
+ 14. Let _instantStart_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _dtStart_, *"compatible"*).
+ 16. Let _endNs_ be ? AddZonedDateTime(_startNs_, _timeZone_, _zonedDateTime_.[[Calendar]], 0, 0, 0, 1, 0, 0, 0, 0, 0, 0).
+ 20. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_roundResult_.[[Year]], [...], _roundResult_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, *"compatible"*, *"prefer"*).
+ sec-temporal-addzoneddatetime step 8:
+ 8. Let _intermediateInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _intermediateDateTime_, *"compatible"*).
+ sec-temporal-builtintimezonegetinstantfor step 1:
+ 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "2001-09-09T00:00:00", // called once on midnight of the input datetime
+ "2001-09-10T00:00:00", // called once on the previous value plus one calendar day
+ "2001-09-09T02:00:00", // called once on the rounding result
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone);
+ datetime.round({ smallestUnit: 'hour' });
+}, expected);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/second/balance-negative-time-units.js b/test/built-ins/Temporal/ZonedDateTime/prototype/second/balance-negative-time-units.js
new file mode 100644
index 00000000000..79e91712fd6
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/second/balance-negative-time-units.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.second
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–8:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-get-temporal.zoneddatetime.prototype.second step 6:
+ 6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
+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 = new Temporal.TimeZone("-00:00:00.000000002");
+const datetime = new Temporal.ZonedDateTime(1_000_000_001n, tz);
+
+assert.sameValue(datetime.second, 0);
+assert.sameValue(datetime.millisecond, 999);
+assert.sameValue(datetime.microsecond, 999);
+assert.sameValue(datetime.nanosecond, 999);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/second/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/second/prop-desc.js
new file mode 100644
index 00000000000..8d768a3f48d
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/second/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.second
+description: The "second" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "second");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..64583eff4ce
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.second
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.second);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..2b4dfed4661
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.second
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.second);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..0a183139f35
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.second
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.second);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/balance-negative-time-units.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/balance-negative-time-units.js
new file mode 100644
index 00000000000..801b00cd19e
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/balance-negative-time-units.js
@@ -0,0 +1,54 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-differencetime step 8:
+ 8. Let _bt_ be ? BalanceTime(_hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal-differenceisodatetime step 2:
+ 2. Let _timeDifference_ be ? DifferenceTime(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_).
+ sec-temporal-differencezoneddatetime step 7:
+ 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_).
+ sec-temporal.zoneddatetime.prototype.since step 16:
+ 16. Let _difference_ be ? DifferenceZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _other_.[[Nanoseconds]], _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]], _largestUnit_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const datetime = new Temporal.ZonedDateTime(830998861_001_001_001n, timeZone);
+const options = { largestUnit: "days" };
+
+const result1 = datetime.since(new Temporal.ZonedDateTime(830995200_000_000_002n, timeZone), options);
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = datetime.since(new Temporal.ZonedDateTime(830995200_000_002_000n, timeZone), options);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = datetime.since(new Temporal.ZonedDateTime(830995200_002_000_000n, timeZone), options);
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = datetime.since(new Temporal.ZonedDateTime(830995202_000_000_000n, timeZone), options);
+TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = datetime.since(new Temporal.ZonedDateTime(830995320_000_000_000n, timeZone), options);
+TemporalHelpers.assertDuration(result5, 0, 0, 0, 0, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+// This one is different because hours are later balanced again in BalanceDuration
+const result6 = datetime.since(new Temporal.ZonedDateTime(831002400_000_000_000n, timeZone), options);
+TemporalHelpers.assertDuration(result6, 0, 0, 0, 0, 0, -58, -58, -998, -998, -999, "hours balance");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/builtin.js
new file mode 100644
index 00000000000..af12cecb759
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.since
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.since),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.since),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.since),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.since.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-copy-of-options.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-copy-of-options.js
new file mode 100644
index 00000000000..46e69aef297
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-copy-of-options.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: The dateUntil() method on the calendar is called with a copy of the options bag
+features: [Temporal]
+---*/
+
+const originalOptions = {
+ largestUnit: "year",
+ shouldBeCopied: {},
+};
+let called = false;
+
+class Calendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateUntil(d1, d2, options) {
+ called = true;
+ assert.notSameValue(options, originalOptions, "options bag should be a copy");
+ assert.sameValue(options.shouldBeCopied, originalOptions.shouldBeCopied, "options bag should be a shallow copy");
+ return new Temporal.Duration(-1);
+ }
+}
+const calendar = new Calendar();
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+// exactly one year later; avoids NanosecondsToDays path
+const later = new Temporal.ZonedDateTime(1_031_536_000_000_000_000n, "UTC", calendar);
+later.since(earlier, originalOptions);
+assert(called, "calendar.dateUntil must be called");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 00000000000..6365c6224c6
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,114 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.zoneddatetime.prototype.since steps 14–18:
+ 14. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ c. Return ...
+ 15. ...
+ 16. Let _difference_ be ? DifferenceZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _other_.[[Nanoseconds]], _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]], _largestUnit_).
+ 17. Let _roundResult_ be ? RoundDuration(_difference_.[[Years]], _difference_.[[Months]], _difference_.[[Weeks]], _difference_.[[Days]], _difference_.[[Hours]], _difference_.[[Minutes]], _difference_.[[Seconds]], _difference_.[[Milliseconds]], _difference_.[[Microseconds]], _difference_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _zonedDateTime_).
+ 18. Let _result_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _zonedDateTime_).
+ sec-temporal-differencezoneddatetime steps 7 and 11:
+ 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_).
+ 11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_).
+ sec-temporal-roundduration steps 5.d and 8.n–p:
+ 5. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
+ ...
+ 8. If _unit_ is *"year"*, then
+ ...
+ n. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
+ o. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"year"*).
+ p. Let _timePassed_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _daysLater_, _untilOptions_)
+ sec-temporal-adjustroundeddurationdays steps 1 and 9:
+ 1. If _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot; or _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*; or _unit_ is *"nanosecond"* and _increment_ is 1, then
+ a. Return ...
+ ...
+ 9. Let _adjustedDateDuration_ be ? AddDuration(_years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0, 0, 0, 0, _direction_, 0, 0, 0, 0, 0, 0, _relativeTo_).
+ sec-temporal-addduration step 7.a–g:
+ a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot.
+ ...
+ f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ g. Else,
+ i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
+ sec-temporal-nanosecondstodays step 11:
+ 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*).
+ sec-temporal-differenceisodatetime steps 9–11:
+ 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
+ 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
+ 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+ const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC", calendar);
+ later.since(earlier, { largestUnit });
+ },
+ {
+ years: ["year", "day"],
+ months: ["month", "day"],
+ weeks: ["week", "day"],
+ days: ["day", "day"],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+// Additionally check the path that goes through AdjustRoundedDurationDays
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.ZonedDateTime(0n, "UTC", calendar);
+ const later = new Temporal.ZonedDateTime(86_399_999_999_999n, "UTC", calendar);
+ later.since(earlier, { largestUnit, roundingIncrement: 2, roundingMode: 'ceil' });
+ },
+ {
+ years: ["year", "day", "day", "day"],
+ months: ["month", "day", "day", "day"],
+ weeks: ["week", "day", "day", "day"],
+ days: ["day", "day", "day", "day"],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+// Also check the path that goes through RoundDuration when smallestUnit is
+// given
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, smallestUnit) => {
+ const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+ const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC", calendar);
+ later.since(earlier, { smallestUnit });
+ },
+ {
+ years: ["year", "day", "day", "year"],
+ months: ["month", "day", "day"],
+ weeks: ["week", "day", "day"],
+ days: ["day", "day", "day"],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-fields-iterable.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-fields-iterable.js
new file mode 100644
index 00000000000..1f8eb2aca6c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-fields-iterable.js
@@ -0,0 +1,39 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.zoneddatetime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "hour",
+ "microsecond",
+ "millisecond",
+ "minute",
+ "month",
+ "monthCode",
+ "nanosecond",
+ "second",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+datetime.since({ year: 2005, month: 6, day: 2, timeZone: "UTC", calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-temporal-object.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-temporal-object.js
new file mode 100644
index 00000000000..8db1101ec36
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.zoneddatetime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", temporalObject);
+ datetime.since({ year: 2005, month: 6, day: 2, timeZone: "UTC", calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/infinity-throws-rangeerror.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..810206a0bec
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.zoneddatetime.prototype.since
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.since({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.since({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-invalid-string.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-invalid-string.js
new file mode 100644
index 00000000000..fca0796eb11
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+assert.throws(RangeError, () => later.since(earlier, { largestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-plurals-accepted.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-plurals-accepted.js
new file mode 100644
index 00000000000..b33c2dce5c7
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-plurals-accepted.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC");
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => later.since(earlier, { largestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-undefined.js
new file mode 100644
index 00000000000..9b532ebd712
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+
+const explicit = later.since(earlier, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default largestUnit is hour");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default largestUnit is hour");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-wrong-type.js
new file mode 100644
index 00000000000..6b8603ca10b
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "year",
+ (largestUnit) => later.since(earlier, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 1, 1, 1, 1, 987, 654, 321, descr),
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/length.js
new file mode 100644
index 00000000000..e311189992f
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: Temporal.ZonedDateTime.prototype.since.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.since, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/name.js
new file mode 100644
index 00000000000..70c5ff9cdd1
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: Temporal.ZonedDateTime.prototype.since.name is "since".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.since, "name", {
+ value: "since",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/negative-epochnanoseconds.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/negative-epochnanoseconds.js
new file mode 100644
index 00000000000..5c92ab45a2b
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/negative-epochnanoseconds.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const result = datetime.since(new Temporal.ZonedDateTime(0n, "UTC"), { largestUnit: "month" });
+TemporalHelpers.assertDuration(result, 0, -5, 0, -7, -7, -9, -24, -999, -999, -999);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/not-a-constructor.js
new file mode 100644
index 00000000000..b620d4079f1
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: >
+ Temporal.ZonedDateTime.prototype.since does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.since();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.since), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.since)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/options-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/options-undefined.js
new file mode 100644
index 00000000000..562654dfb45
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/options-undefined.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: Verify that undefined options are handled correctly.
+features: [BigInt, Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(957270896_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(959949296_987_654_322n, "UTC");
+
+const explicit = later.since(earlier, undefined);
+assert.sameValue(explicit.years, 0, "default largest unit is hours");
+assert.sameValue(explicit.months, 0, "default largest unit is hours");
+assert.sameValue(explicit.weeks, 0, "default largest unit is hours");
+assert.sameValue(explicit.days, 0, "default largest unit is hours");
+assert.sameValue(explicit.hours, 744, "default largest unit is hours");
+assert.sameValue(explicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
+
+const implicit = later.since(earlier);
+assert.sameValue(implicit.years, 0, "default largest unit is hours");
+assert.sameValue(implicit.months, 0, "default largest unit is hours");
+assert.sameValue(implicit.weeks, 0, "default largest unit is hours");
+assert.sameValue(implicit.days, 0, "default largest unit is hours");
+assert.sameValue(implicit.hours, 744, "default largest unit is hours");
+assert.sameValue(implicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/prop-desc.js
new file mode 100644
index 00000000000..8fa77430649
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: The "since" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.since,
+ "function",
+ "`typeof ZonedDateTime.prototype.since` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "since", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/read-time-fields-before-datefromfields.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/read-time-fields-before-datefromfields.js
new file mode 100644
index 00000000000..dee3a85d412
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/read-time-fields-before-datefromfields.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.zoneddatetime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 2.j:
+ j. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const duration = datetime.since({ year: 2001, month: 9, day: 9, timeZone: "UTC", calendar });
+
+TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, 1, 46, 40, 987, 654, 321);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-nan.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-nan.js
new file mode 100644
index 00000000000..071cf4c5bdb
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-nan.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.zoneddatetime.prototype.since step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_988_655_322n, "UTC");
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: NaN }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-non-integer.js
new file mode 100644
index 00000000000..d228d8fcef7
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-non-integer.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: Rounding for roundingIncrement option
+info: |
+ sec-temporal-totemporalroundingincrement step 7:
+ 7. Set _increment_ to floor(ℝ(_increment_)).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_000_000_000_000_005n, "UTC");
+const result = later.since(earlier, { roundingIncrement: 2.5 });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, "roundingIncrement 2.5 floors to 2");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-out-of-range.js
new file mode 100644
index 00000000000..81bd48e8a14
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-out-of-range.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ sec-temporal-totemporalroundingincrement step 6:
+ 6. If _increment_ < 1 or _increment_ > _maximum_, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_000_000_000_000_005n, "UTC");
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: Infinity }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-undefined.js
new file mode 100644
index 00000000000..9a0bf0fd06e
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.zoneddatetime.prototype.since step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_988_655_322n, "UTC");
+
+const explicit = later.since(earlier, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 25, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 25, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-wrong-type.js
new file mode 100644
index 00000000000..ee09f927421
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.zoneddatetime.prototype.since step 13:
+ 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_988_655_322n, "UTC");
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => later.since(earlier, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 1, 1, 1, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 1, 1, 0, descr),
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-invalid-string.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-invalid-string.js
new file mode 100644
index 00000000000..8fd11a6ba56
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_123_987_500n, "UTC");
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "microsecond", roundingMode: "other string" }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-undefined.js
new file mode 100644
index 00000000000..7c18e02511c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-undefined.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_123_987_500n, "UTC");
+
+const explicit1 = later.since(earlier, { smallestUnit: "microsecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 0, 25, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+const implicit1 = later.since(earlier, { smallestUnit: "microsecond" });
+TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 0, 25, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+
+const explicit2 = later.since(earlier, { smallestUnit: "millisecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 0, 25, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+const implicit2 = later.since(earlier, { smallestUnit: "millisecond" });
+TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 0, 25, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+
+const explicit3 = later.since(earlier, { smallestUnit: "second", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit3, 0, 0, 0, 0, 25, 1, 1, 0, 0, 0, "default roundingMode is trunc");
+const implicit3 = later.since(earlier, { smallestUnit: "second" });
+TemporalHelpers.assertDuration(implicit3, 0, 0, 0, 0, 25, 1, 1, 0, 0, 0, "default roundingMode is trunc");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-wrong-type.js
new file mode 100644
index 00000000000..a913aeeaf48
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_123_987_500n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => later.since(earlier, { smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 123, 987, 0, descr),
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-invalid-string.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-invalid-string.js
new file mode 100644
index 00000000000..16ced4c28d0
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-plurals-accepted.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-plurals-accepted.js
new file mode 100644
index 00000000000..c152543d978
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-plurals-accepted.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC");
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => later.since(earlier, { smallestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-undefined.js
new file mode 100644
index 00000000000..a2c0bca6564
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+
+const explicit = later.since(earlier, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-wrong-type.js
new file mode 100644
index 00000000000..7c3528d053b
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => later.since(earlier, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 987, 654, 0, descr),
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..0169317fa45
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const other = new Temporal.ZonedDateTime(1_100_000_000_123_456_789n, timeZone);
+ assert.throws(RangeError, () => datetime.since(other, { largestUnit: "days" }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..1a9e114693e
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_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 other = new Temporal.ZonedDateTime(1_100_000_000_123_456_789n, timeZone);
+ assert.throws(RangeError, () => datetime.since(other, { largestUnit: "days" }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..3dffda84fa9
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const other = new Temporal.ZonedDateTime(1_100_000_000_123_456_789n, timeZone);
+ assert.throws(TypeError, () => datetime.since(other, { largestUnit: "days" }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getpossibleinstantsfor-iterable.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 00000000000..7497e4d67ac
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,38 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.zoneddatetime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 7:
+ 7. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Not called on the instance's time zone
+
+const expected1 = [];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone);
+ datetime.since({ year: 2005, month: 6, day: 2, timeZone: "UTC" });
+}, expected1);
+
+// Called on the argument's time zone
+
+const expected2 = [
+ "2005-06-02T00:00:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+ datetime.since({ year: 2005, month: 6, day: 2, timeZone });
+}, expected2);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-string-datetime.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-string-datetime.js
new file mode 100644
index 00000000000..636d0b1b4fb
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-string-datetime.js
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let expectedTimeZone = "UTC";
+const instance1 = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance1.since({ year: 2020, month: 5, day: 2, timeZone }), "bare date-time string is not a time zone");
+assert.throws(RangeError, () => instance1.since({ year: 2020, month: 5, day: 2, timeZone: { timeZone } }), "bare date-time string is not a time zone");
+
+// The following are all valid strings so should not throw. They should produce
+// expectedTimeZone, so additionally the operation will not throw due to the
+// time zones being different on the receiver and the argument.
+
+timeZone = "2021-08-19T17:30Z";
+instance1.since({ year: 2020, month: 5, day: 2, timeZone });
+instance1.since({ year: 2020, month: 5, day: 2, timeZone: { timeZone } });
+
+expectedTimeZone = "-07:00";
+const instance2 = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+timeZone = "2021-08-19T17:30-07:00";
+instance2.since({ year: 2020, month: 5, day: 2, timeZone });
+instance2.since({ year: 2020, month: 5, day: 2, timeZone: { timeZone } });
+
+expectedTimeZone = "America/Vancouver";
+const instance3 = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+timeZone = "2021-08-19T17:30[America/Vancouver]";
+instance3.since({ year: 2020, month: 5, day: 2, timeZone });
+instance3.since({ year: 2020, month: 5, day: 2, timeZone: { timeZone } });
+
+timeZone = "2021-08-19T17:30Z[America/Vancouver]";
+instance3.since({ year: 2020, month: 5, day: 2, timeZone });
+instance3.since({ year: 2020, month: 5, day: 2, timeZone: { timeZone } });
+
+timeZone = "2021-08-19T17:30-07:00[America/Vancouver]";
+instance3.since({ year: 2020, month: 5, day: 2, timeZone });
+instance3.since({ year: 2020, month: 5, day: 2, timeZone: { timeZone } });
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/zoneddatetime-string.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/zoneddatetime-string.js
new file mode 100644
index 00000000000..f4b2daf362b
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/zoneddatetime-string.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.since
+description: Conversion of ISO date-time strings to Temporal.ZonedDateTime instances
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.since(str), "bare date-time string is not a ZonedDateTime");
+str = "1970-01-01T00:00Z";
+assert.throws(RangeError, () => instance.since(str), "date-time + Z is not a ZonedDateTime");
+str = "1970-01-01T00:00+01:00";
+assert.throws(RangeError, () => instance.since(str), "date-time + offset is not a ZonedDateTime");
+
+str = "1970-01-01T00:00[Europe/Berlin]";
+const result1 = instance.since(str);
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, "date-time + IANA annotation preserves wall time in the time zone");
+
+str = "1970-01-01T00:00Z[Europe/Berlin]";
+const result2 = instance.since(str);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation preserves exact time in the time zone");
+
+str = "1970-01-01T00:00+01:00[Europe/Berlin]";
+const result3 = instance.since(str);
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, "date-time + offset + IANA annotation ensures both exact and wall time match");
+
+str = "1970-01-01T00:00-04:15[Europe/Berlin]";
+assert.throws(RangeError, () => instance.since(str), "date-time + offset + IANA annotation throws if wall time and exact time mismatch");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/builtin.js
new file mode 100644
index 00000000000..c7c1636a098
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.startofday
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.startOfDay
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.startOfDay),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.startOfDay),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.startOfDay),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.startOfDay.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/calendar-dateadd-called-with-options-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/calendar-dateadd-called-with-options-undefined.js
new file mode 100644
index 00000000000..0fb6b7e5a72
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/calendar-dateadd-called-with-options-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.startofday
+description: >
+ BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the
+ options value
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddUndefinedOptions();
+const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9);
+const instance = new Temporal.ZonedDateTime(7200_000_000_000n, timeZone, calendar);
+instance.startOfDay();
+assert.sameValue(calendar.dateAddCallCount, 1);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/length.js
new file mode 100644
index 00000000000..8c2c6c8805c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.startofday
+description: Temporal.ZonedDateTime.prototype.startOfDay.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.startOfDay, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/name.js
new file mode 100644
index 00000000000..e6103ae0c80
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.startofday
+description: Temporal.ZonedDateTime.prototype.startOfDay.name is "startOfDay".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.startOfDay, "name", {
+ value: "startOfDay",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/not-a-constructor.js
new file mode 100644
index 00000000000..d5d48ab833f
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.startofday
+description: >
+ Temporal.ZonedDateTime.prototype.startOfDay does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.startOfDay();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.startOfDay), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.startOfDay)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/prop-desc.js
new file mode 100644
index 00000000000..bc70a596fa0
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.startofday
+description: The "startOfDay" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.startOfDay,
+ "function",
+ "`typeof ZonedDateTime.prototype.startOfDay` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "startOfDay", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/subclassing-ignored.js b/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/subclassing-ignored.js
new file mode 100644
index 00000000000..1ba3f80addb
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/subclassing-ignored.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.startofday
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.ZonedDateTime,
+ [88000_123_456_789n, "UTC"],
+ "startOfDay",
+ [],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 86400_000_000_000n, "epochNanoseconds result");
+ assert.sameValue(result.year, 1970, "year result");
+ assert.sameValue(result.month, 1, "month result");
+ assert.sameValue(result.day, 2, "day result");
+ assert.sameValue(result.hour, 0, "hour result");
+ assert.sameValue(result.minute, 0, "minute result");
+ assert.sameValue(result.second, 0, "second result");
+ assert.sameValue(result.millisecond, 0, "millisecond result");
+ assert.sameValue(result.microsecond, 0, "microsecond result");
+ assert.sameValue(result.nanosecond, 0, "nanosecond result");
+ },
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..ec6fb2cadba
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.startofday
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.startOfDay());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..991c3e89e01
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.startofday
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.startOfDay());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..0140d3b9ffc
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.startofday
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.startOfDay());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-string-negative-fractional-units.js b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-string-negative-fractional-units.js
new file mode 100644
index 00000000000..5ccf12840ef
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-string-negative-fractional-units.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.subtract
+description: Strings with fractional duration units are treated with the correct sign
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+const resultHours = instance.subtract("-PT24.567890123H");
+assert.sameValue(resultHours.epochNanoseconds, 1_000_088_444_404_442_799n, "negative fractional hours");
+
+const resultMinutes = instance.subtract("-PT1440.567890123M");
+assert.sameValue(resultMinutes.epochNanoseconds, 1_000_086_434_073_407_379n, "negative fractional minutes");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin.js
new file mode 100644
index 00000000000..67275fd17b9
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.subtract
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.subtract
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.subtract),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.subtract),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.subtract),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.subtract.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/length.js
new file mode 100644
index 00000000000..5f87ed676a8
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.subtract
+description: Temporal.ZonedDateTime.prototype.subtract.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.subtract, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/name.js
new file mode 100644
index 00000000000..4e5888b44e2
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.subtract
+description: Temporal.ZonedDateTime.prototype.subtract.name is "subtract".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.subtract, "name", {
+ value: "subtract",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/negative-epochnanoseconds.js b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/negative-epochnanoseconds.js
new file mode 100644
index 00000000000..478add81bd2
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/negative-epochnanoseconds.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.subtract
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const result = datetime.subtract(new Temporal.Duration(0, 0, 0, 1));
+assert.sameValue(result.epochNanoseconds, -13936164_999_999_999n);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/non-integer-throws-rangeerror.js b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/non-integer-throws-rangeerror.js
new file mode 100644
index 00000000000..4534b39f709
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/non-integer-throws-rangeerror.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.subtract
+description: A non-integer value for any recognized property in the property bag, throws a RangeError
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const fields = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds",
+];
+fields.forEach((field) => {
+ assert.throws(RangeError, () => instance.subtract({ [field]: 1.5 }));
+ assert.throws(RangeError, () => instance.subtract({ [field]: -1.5 }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/not-a-constructor.js
new file mode 100644
index 00000000000..02e9b631eef
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.subtract
+description: >
+ Temporal.ZonedDateTime.prototype.subtract does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.subtract();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.subtract), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.subtract)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/options-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/options-undefined.js
new file mode 100644
index 00000000000..96e8b044109
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/options-undefined.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.subtract
+description: Verify that undefined options are handled correctly.
+features: [BigInt, Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(954506096_987_654_321n, "UTC");
+const duration = { months: 1 };
+
+const explicit = datetime.subtract(duration, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = datetime.subtract(duration);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-invalid-string.js b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-invalid-string.js
new file mode 100644
index 00000000000..8c144ef70d0
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-invalid-string.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.subtract
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-addzoneddatetime step 6:
+ 6. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.zoneddatetime.prototype.subtract step 7:
+ 7. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZone_, _calendar_, −_duration_.[[Years]], −_duration_.[[Months]], −_duration_.[[Weeks]], −_duration_.[[Days]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]], _options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const duration = new Temporal.Duration(0, 0, 0, 1);
+assert.throws(RangeError, () => datetime.subtract(duration, { overflow: "other string" }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-undefined.js
new file mode 100644
index 00000000000..b585fbe0d3d
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-undefined.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.subtract
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-addzoneddatetime step 6:
+ 6. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.zoneddatetime.prototype.subtract step 7:
+ 7. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZone_, _calendar_, −_duration_.[[Years]], −_duration_.[[Months]], −_duration_.[[Weeks]], −_duration_.[[Days]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]], _options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-1n, "UTC");
+const duration = new Temporal.Duration(0, 1);
+
+const explicit = datetime.subtract(duration, { overflow: undefined });
+assert.sameValue(explicit.epochNanoseconds, -2678400_000_000_001n, "default overflow is constrain");
+const implicit = datetime.subtract(duration, {});
+assert.sameValue(implicit.epochNanoseconds, -2678400_000_000_001n, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-wrong-type.js
new file mode 100644
index 00000000000..1827aef0f55
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-wrong-type.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.subtract
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal.calendar.prototype.dateadd step 7:
+ 7. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal-addzoneddatetime step 6:
+ 6. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
+ sec-temporal.zoneddatetime.prototype.subtract step 7:
+ 7. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZone_, _calendar_, −_duration_.[[Years]], −_duration_.[[Months]], −_duration_.[[Weeks]], −_duration_.[[Days]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]], _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const duration = new Temporal.Duration(0, 0, 0, 1);
+TemporalHelpers.checkStringOptionWrongType("overflow", "constrain",
+ (overflow) => datetime.subtract(duration, { overflow }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 999_913_600_987_654_321n, descr),
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/prop-desc.js
new file mode 100644
index 00000000000..2392c28a7b4
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.subtract
+description: The "subtract" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.subtract,
+ "function",
+ "`typeof ZonedDateTime.prototype.subtract` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "subtract", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/subclassing-ignored.js b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/subclassing-ignored.js
new file mode 100644
index 00000000000..0acc781ce7c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/subclassing-ignored.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.subtract
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.ZonedDateTime,
+ [10n, "UTC"],
+ "subtract",
+ [{ nanoseconds: 5 }],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 5n, "epochNanoseconds result");
+ assert.sameValue(result.year, 1970, "year result");
+ assert.sameValue(result.month, 1, "month result");
+ assert.sameValue(result.day, 1, "day result");
+ assert.sameValue(result.hour, 0, "hour result");
+ assert.sameValue(result.minute, 0, "minute result");
+ assert.sameValue(result.second, 0, "second result");
+ assert.sameValue(result.millisecond, 0, "millisecond result");
+ assert.sameValue(result.microsecond, 0, "microsecond result");
+ assert.sameValue(result.nanosecond, 5, "nanosecond result");
+ },
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..043be5d89ba
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.subtract
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.subtract(duration));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..40ec8ae7c80
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.subtract
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.subtract(duration));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..1f60290951c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.subtract
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const duration = new Temporal.Duration(1);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.subtract(duration));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/timeZone/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/timeZone/prop-desc.js
new file mode 100644
index 00000000000..e01a0f9d659
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/timeZone/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.timezone
+description: The "timeZone" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "timeZone");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toInstant/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toInstant/builtin.js
new file mode 100644
index 00000000000..fb5f7ace5fb
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toInstant/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toinstant
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.toInstant
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.toInstant),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toInstant),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toInstant),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.toInstant.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toInstant/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toInstant/length.js
new file mode 100644
index 00000000000..29ffe237c8f
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toInstant/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toinstant
+description: Temporal.ZonedDateTime.prototype.toInstant.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.toInstant, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toInstant/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toInstant/name.js
new file mode 100644
index 00000000000..05515678f87
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toInstant/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toinstant
+description: Temporal.ZonedDateTime.prototype.toInstant.name is "toInstant".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.toInstant, "name", {
+ value: "toInstant",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toInstant/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toInstant/not-a-constructor.js
new file mode 100644
index 00000000000..011d2727763
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toInstant/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toinstant
+description: >
+ Temporal.ZonedDateTime.prototype.toInstant does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.toInstant();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toInstant), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.toInstant)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toInstant/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toInstant/prop-desc.js
new file mode 100644
index 00000000000..884180a4ea5
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toInstant/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toinstant
+description: The "toInstant" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.toInstant,
+ "function",
+ "`typeof ZonedDateTime.prototype.toInstant` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "toInstant", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/balance-negative-time-units.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/balance-negative-time-units.js
new file mode 100644
index 00000000000..8fe92a5dfa8
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/balance-negative-time-units.js
@@ -0,0 +1,40 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tojson
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-temporalzoneddatetimetostring step 9:
+ 9. Let _dateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _isoCalendar_).
+ sec-get-temporal.zoneddatetime.prototype.tojson step 3:
+ 3. Return ? TemporalZonedDateTimeToString(_zonedDateTime_, *"auto"*, *"auto"*, *"auto"*, *"auto"*).
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// ZonedDateTime
+const tz = new Temporal.TimeZone("-00:00:00.000000002");
+const datetime = new Temporal.ZonedDateTime(1001n, tz);
+
+const jsonString = datetime.toJSON();
+
+assert.sameValue(jsonString, "1970-01-01T00:00:00.000000999-00:00:00.000000002[-00:00:00.000000002]");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin.js
new file mode 100644
index 00000000000..279e0cb4326
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tojson
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.toJSON
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.toJSON),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toJSON),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toJSON),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.toJSON.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/length.js
new file mode 100644
index 00000000000..dc1f67edb05
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tojson
+description: Temporal.ZonedDateTime.prototype.toJSON.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.toJSON, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/name.js
new file mode 100644
index 00000000000..34b158fe63a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tojson
+description: Temporal.ZonedDateTime.prototype.toJSON.name is "toJSON".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.toJSON, "name", {
+ value: "toJSON",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/negative-epochnanoseconds.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/negative-epochnanoseconds.js
new file mode 100644
index 00000000000..fb73ef987fe
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/negative-epochnanoseconds.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tojson
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const result = datetime.toJSON();
+assert.sameValue(result, "1969-07-24T16:50:35.000000001+00:00[UTC]");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/not-a-constructor.js
new file mode 100644
index 00000000000..f963d13ff67
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tojson
+description: >
+ Temporal.ZonedDateTime.prototype.toJSON does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.toJSON();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toJSON), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.toJSON)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/prop-desc.js
new file mode 100644
index 00000000000..26f62c10587
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tojson
+description: The "toJSON" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.toJSON,
+ "function",
+ "`typeof ZonedDateTime.prototype.toJSON` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "toJSON", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..a30ed166433
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tojson
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toJSON());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..331ed09ad2c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tojson
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toJSON());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..d0cca238d42
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tojson
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.toJSON());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin.js
new file mode 100644
index 00000000000..13ae4dae7c4
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tolocalestring
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.toLocaleString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.toLocaleString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toLocaleString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toLocaleString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.toLocaleString.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/length.js
new file mode 100644
index 00000000000..00f5d03d032
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tolocalestring
+description: Temporal.ZonedDateTime.prototype.toLocaleString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.toLocaleString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/locales-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/locales-undefined.js
new file mode 100644
index 00000000000..8ed6549fdff
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/locales-undefined.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tolocalestring
+description: Omitting the locales argument defaults to the DateTimeFormat default
+features: [BigInt, Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(957270896_987_650_000n, "UTC");
+const defaultFormatter = new Intl.DateTimeFormat([], Object.create(null));
+const expected = defaultFormatter.format(datetime);
+
+const actualExplicit = datetime.toLocaleString(undefined);
+assert.sameValue(actualExplicit, expected, "default locale is determined by Intl.DateTimeFormat");
+
+const actualImplicit = datetime.toLocaleString();
+assert.sameValue(actualImplicit, expected, "default locale is determined by Intl.DateTimeFormat");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/name.js
new file mode 100644
index 00000000000..089bb5138a1
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tolocalestring
+description: Temporal.ZonedDateTime.prototype.toLocaleString.name is "toLocaleString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.toLocaleString, "name", {
+ value: "toLocaleString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/not-a-constructor.js
new file mode 100644
index 00000000000..ff37dba13bb
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tolocalestring
+description: >
+ Temporal.ZonedDateTime.prototype.toLocaleString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.toLocaleString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toLocaleString), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.toLocaleString)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/options-conflict.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/options-conflict.js
new file mode 100644
index 00000000000..5cc1162dc4b
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/options-conflict.js
@@ -0,0 +1,48 @@
+// Copyright (C) 2021 Kate Miháliková. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sup-temporal.zoneddatetime.prototype.tolocalestring
+description: >
+ Conflicting properties of dateStyle must be rejected with a TypeError for the options argument
+info: |
+ Using sec-temporal-getdatetimeformatpattern:
+ GetDateTimeFormatPattern ( dateStyle, timeStyle, matcher, opt, dataLocaleData, hc )
+
+ 1. If dateStyle is not undefined or timeStyle is not undefined, then
+ a. For each row in Table 7, except the header row, do
+ i. Let prop be the name given in the Property column of the row.
+ ii. Let p be opt.[[]].
+ iii. If p is not undefined, then
+ 1. Throw a TypeError exception.
+features: [BigInt, Temporal]
+---*/
+
+// Table 14 - Supported fields + example value for each field
+const conflictingOptions = [
+ [ "weekday", "short" ],
+ [ "era", "short" ],
+ [ "year", "numeric" ],
+ [ "month", "numeric" ],
+ [ "day", "numeric" ],
+ [ "hour", "numeric" ],
+ [ "minute", "numeric" ],
+ [ "second", "numeric" ],
+ [ "dayPeriod", "short" ],
+ [ "fractionalSecondDigits", 3 ],
+ [ "timeZoneName", "short" ],
+];
+const datetime = new Temporal.ZonedDateTime(957270896_987_650_000n, "UTC");
+
+assert.sameValue(typeof datetime.toLocaleString("en", { dateStyle: "short" }), "string");
+assert.sameValue(typeof datetime.toLocaleString("en", { timeStyle: "short" }), "string");
+
+for (const [ option, value ] of conflictingOptions) {
+ assert.throws(TypeError, function() {
+ datetime.toLocaleString("en", { [option]: value, dateStyle: "short" });
+ }, `datetime.toLocaleString("en", { ${option}: "${value}", dateStyle: "short" }) throws TypeError`);
+
+ assert.throws(TypeError, function() {
+ datetime.toLocaleString("en", { [option]: value, timeStyle: "short" });
+ }, `datetime.toLocaleString("en", { ${option}: "${value}", timeStyle: "short" }) throws TypeError`);
+}
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/options-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/options-undefined.js
new file mode 100644
index 00000000000..64e3f7881df
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/options-undefined.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tolocalestring
+description: Verify that undefined options are handled correctly.
+features: [BigInt, Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(957270896_987_650_000n, "UTC");
+const defaultFormatter = new Intl.DateTimeFormat('en', Object.create(null));
+const expected = defaultFormatter.format(datetime);
+
+const actualExplicit = datetime.toLocaleString('en', undefined);
+assert.sameValue(actualExplicit, expected, "default locale is determined by Intl.DateTimeFormat");
+
+const actualImplicit = datetime.toLocaleString('en');
+assert.sameValue(actualImplicit, expected, "default locale is determined by Intl.DateTimeFormat");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/prop-desc.js
new file mode 100644
index 00000000000..a1ff3fbfba1
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tolocalestring
+description: The "toLocaleString" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.toLocaleString,
+ "function",
+ "`typeof ZonedDateTime.prototype.toLocaleString` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "toLocaleString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/builtin.js
new file mode 100644
index 00000000000..85adedb8278
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaindate
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.toPlainDate
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.toPlainDate),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toPlainDate),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toPlainDate),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.toPlainDate.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/length.js
new file mode 100644
index 00000000000..822dc2f4870
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaindate
+description: Temporal.ZonedDateTime.prototype.toPlainDate.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.toPlainDate, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/name.js
new file mode 100644
index 00000000000..b14310a01d2
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaindate
+description: Temporal.ZonedDateTime.prototype.toPlainDate.name is "toPlainDate".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.toPlainDate, "name", {
+ value: "toPlainDate",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/not-a-constructor.js
new file mode 100644
index 00000000000..cc9b3bc79d6
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaindate
+description: >
+ Temporal.ZonedDateTime.prototype.toPlainDate does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.toPlainDate();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toPlainDate), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.toPlainDate)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/prop-desc.js
new file mode 100644
index 00000000000..0834619a95b
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaindate
+description: The "toPlainDate" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.toPlainDate,
+ "function",
+ "`typeof ZonedDateTime.prototype.toPlainDate` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "toPlainDate", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..8cc238b12ab
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaindate
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toPlainDate());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..c0e42901648
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaindate
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toPlainDate());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..c22035e5390
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaindate
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.toPlainDate());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/balance-negative-time-units.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/balance-negative-time-units.js
new file mode 100644
index 00000000000..788ae268bfc
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/balance-negative-time-units.js
@@ -0,0 +1,39 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaindatetime
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-get-temporal.zoneddatetime.prototype.toplaindatetime step 5:
+ 5. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _zonedDateTime_.[[Calendar]]).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// exact time's epoch parts
+const tz = new Temporal.TimeZone("-00:00:00.000000002");
+const datetime = new Temporal.ZonedDateTime(1001n, tz);
+
+const pdt = datetime.toPlainDateTime();
+
+TemporalHelpers.assertPlainDateTime(pdt, 1970, 1, "M01", 1, 0, 0, 0, 0, 0, 999);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/builtin.js
new file mode 100644
index 00000000000..71091c1a199
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaindatetime
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.toPlainDateTime
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.toPlainDateTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toPlainDateTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toPlainDateTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.toPlainDateTime.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/length.js
new file mode 100644
index 00000000000..22e23c9fbfc
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaindatetime
+description: Temporal.ZonedDateTime.prototype.toPlainDateTime.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.toPlainDateTime, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/name.js
new file mode 100644
index 00000000000..9f5c1bf3e3a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaindatetime
+description: Temporal.ZonedDateTime.prototype.toPlainDateTime.name is "toPlainDateTime".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.toPlainDateTime, "name", {
+ value: "toPlainDateTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/negative-epochnanoseconds.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/negative-epochnanoseconds.js
new file mode 100644
index 00000000000..9b58d44d276
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/negative-epochnanoseconds.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaindatetime
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const result = datetime.toPlainDateTime();
+TemporalHelpers.assertPlainDateTime(result, 1969, 7, "M07", 24, 16, 50, 35, 0, 0, 1);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/not-a-constructor.js
new file mode 100644
index 00000000000..eda48ec409a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaindatetime
+description: >
+ Temporal.ZonedDateTime.prototype.toPlainDateTime does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.toPlainDateTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toPlainDateTime), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.toPlainDateTime)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/plain-custom-timezone.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/plain-custom-timezone.js
new file mode 100644
index 00000000000..51806b01b4d
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/plain-custom-timezone.js
@@ -0,0 +1,41 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaindatetime
+description: timeZone.getOffsetNanosecondsFor() called
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const actual = [];
+const expected = [
+ "has timeZone.timeZone",
+ "get timeZone.getOffsetNanosecondsFor",
+ "call timeZone.getOffsetNanosecondsFor",
+];
+
+const timeZone = new Proxy({
+ getOffsetNanosecondsFor() {
+ actual.push("call timeZone.getOffsetNanosecondsFor");
+ return -8735135802468;
+ },
+}, {
+ has(target, property) {
+ actual.push(`has timeZone.${property}`);
+ return property in target;
+ },
+ get(target, property) {
+ actual.push(`get timeZone.${property}`);
+ return target[property];
+ },
+});
+
+const zdt = new Temporal.ZonedDateTime(160583136123456789n, timeZone);
+const dateTime = Temporal.PlainDateTime.from("1975-02-02T12:00:00.987654321");
+const result = zdt.toPlainDateTime();
+for (const property of ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"]) {
+ assert.sameValue(result[property], dateTime[property], property);
+}
+
+assert.compareArray(actual, expected);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/prop-desc.js
new file mode 100644
index 00000000000..6928a01968a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaindatetime
+description: The "toPlainDateTime" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.toPlainDateTime,
+ "function",
+ "`typeof ZonedDateTime.prototype.toPlainDateTime` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "toPlainDateTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..888ebc14d93
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaindatetime
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toPlainDateTime());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..f312c139857
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaindatetime
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toPlainDateTime());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..f559b4aa2e9
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaindatetime
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.toPlainDateTime());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin.js
new file mode 100644
index 00000000000..b9abcadc788
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplainmonthday
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.toPlainMonthDay
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.toPlainMonthDay),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toPlainMonthDay),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toPlainMonthDay),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.toPlainMonthDay.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-arguments.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-arguments.js
new file mode 100644
index 00000000000..8fe60d2cc60
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-arguments.js
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplainmonthday
+description: Correct options value is passed to calendar method
+info: |
+ MonthDayFromFields ( calendar, fields [ , options ] )
+
+ 3. If options is not present, then
+ a. Set options to undefined.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthDayFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.sameValue(args[1], undefined, "args[1]");
+ return super.monthDayFromFields(...args);
+ }
+}
+const zonedDateTime = new Temporal.ZonedDateTime(957270896123456789n, "UTC", new CustomCalendar());
+const result = zonedDateTime.toPlainMonthDay();
+TemporalHelpers.assertPlainMonthDay(result, "M05", 2);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-fields-iterable.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-fields-iterable.js
new file mode 100644
index 00000000000..d542b9f3ccb
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-fields-iterable.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplainmonthday
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.zoneddatetime.prototype.toplainmonthday step 7:
+ 7. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"monthCode"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "monthCode",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+datetime.toPlainMonthDay();
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-result.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-result.js
new file mode 100644
index 00000000000..c03cfbff763
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-result.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplainmonthday
+description: TypeError thrown when calendar method returns an object with the wrong brand
+info: |
+ MonthDayFromFields ( calendar, fields, options )
+
+ 4. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ monthDayFromFields() {
+ return {};
+ }
+}
+const zonedDateTime = new Temporal.ZonedDateTime(957270896123456789n, "UTC", new CustomCalendar());
+assert.throws(TypeError, () => zonedDateTime.toPlainMonthDay());
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/length.js
new file mode 100644
index 00000000000..f1d40d3b229
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplainmonthday
+description: Temporal.ZonedDateTime.prototype.toPlainMonthDay.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.toPlainMonthDay, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/name.js
new file mode 100644
index 00000000000..06b551c3c47
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplainmonthday
+description: Temporal.ZonedDateTime.prototype.toPlainMonthDay.name is "toPlainMonthDay".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.toPlainMonthDay, "name", {
+ value: "toPlainMonthDay",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/not-a-constructor.js
new file mode 100644
index 00000000000..def4383625f
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplainmonthday
+description: >
+ Temporal.ZonedDateTime.prototype.toPlainMonthDay does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.toPlainMonthDay();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toPlainMonthDay), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.toPlainMonthDay)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/prop-desc.js
new file mode 100644
index 00000000000..bebb091b1b9
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplainmonthday
+description: The "toPlainMonthDay" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.toPlainMonthDay,
+ "function",
+ "`typeof ZonedDateTime.prototype.toPlainMonthDay` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "toPlainMonthDay", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..3c239463449
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplainmonthday
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toPlainMonthDay());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..4a34249f137
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplainmonthday
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toPlainMonthDay());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..334a531ba07
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplainmonthday
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.toPlainMonthDay());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/balance-negative-time-units.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/balance-negative-time-units.js
new file mode 100644
index 00000000000..ade5862d6cf
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/balance-negative-time-units.js
@@ -0,0 +1,39 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaintime
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-get-temporal.zoneddatetime.prototype.toplaintime step 5:
+ 5. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _zonedDateTime_.[[Calendar]]).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// exact time's epoch parts
+const tz = new Temporal.TimeZone("-00:00:00.000000002");
+const datetime = new Temporal.ZonedDateTime(1001n, tz);
+
+const time = datetime.toPlainTime();
+
+TemporalHelpers.assertPlainTime(time, 0, 0, 0, 0, 0, 999);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/builtin.js
new file mode 100644
index 00000000000..0ff83131bdd
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaintime
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.toPlainTime
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.toPlainTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toPlainTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toPlainTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.toPlainTime.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/length.js
new file mode 100644
index 00000000000..e7f23ef26f3
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaintime
+description: Temporal.ZonedDateTime.prototype.toPlainTime.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.toPlainTime, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/name.js
new file mode 100644
index 00000000000..9868d6b3e6a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaintime
+description: Temporal.ZonedDateTime.prototype.toPlainTime.name is "toPlainTime".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.toPlainTime, "name", {
+ value: "toPlainTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/negative-epochnanoseconds.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/negative-epochnanoseconds.js
new file mode 100644
index 00000000000..3f7b3284f35
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/negative-epochnanoseconds.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaintime
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const result = datetime.toPlainTime();
+TemporalHelpers.assertPlainTime(result, 16, 50, 35, 0, 0, 1);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/not-a-constructor.js
new file mode 100644
index 00000000000..cd5ba5e5cbb
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaintime
+description: >
+ Temporal.ZonedDateTime.prototype.toPlainTime does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.toPlainTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toPlainTime), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.toPlainTime)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/prop-desc.js
new file mode 100644
index 00000000000..ec95b8b7f1c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaintime
+description: The "toPlainTime" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.toPlainTime,
+ "function",
+ "`typeof ZonedDateTime.prototype.toPlainTime` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "toPlainTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..8ac9757f363
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaintime
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toPlainTime());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..613c7093ffd
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaintime
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toPlainTime());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..f5c4a4f5480
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplaintime
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.toPlainTime());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin.js
new file mode 100644
index 00000000000..9446a1db131
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.toPlainYearMonth
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.toPlainYearMonth),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toPlainYearMonth),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toPlainYearMonth),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.toPlainYearMonth.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-arguments.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-arguments.js
new file mode 100644
index 00000000000..4cf9c1f921c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-arguments.js
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth
+description: Correct options value is passed to calendar method
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 3. If options is not present, then
+ a. Set options to undefined.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ yearMonthFromFields(...args) {
+ assert.sameValue(args.length, 2, "args.length");
+ assert.sameValue(typeof args[0], "object", "args[0]");
+ assert.sameValue(args[1], undefined, "args[1]");
+ return super.yearMonthFromFields(...args);
+ }
+}
+const zonedDateTime = new Temporal.ZonedDateTime(957270896123456789n, "UTC", new CustomCalendar());
+const result = zonedDateTime.toPlainYearMonth();
+TemporalHelpers.assertPlainYearMonth(result, 2000, 5, "M05");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-fields-iterable.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-fields-iterable.js
new file mode 100644
index 00000000000..9b096ac6e05
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-fields-iterable.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.zoneddatetime.prototype.toplainyearmonth step 7:
+ 7. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "monthCode",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+datetime.toPlainYearMonth();
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-result.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-result.js
new file mode 100644
index 00000000000..61a67fd44f1
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-result.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth
+description: TypeError thrown when calendar method returns an object with the wrong brand
+info: |
+ YearMonthFromFields ( calendar, fields [ , options ] )
+
+ 4. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
+features: [Temporal]
+---*/
+
+class CustomCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ yearMonthFromFields() {
+ return {};
+ }
+}
+const zonedDateTime = new Temporal.ZonedDateTime(957270896123456789n, "UTC", new CustomCalendar());
+assert.throws(TypeError, () => zonedDateTime.toPlainYearMonth());
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/length.js
new file mode 100644
index 00000000000..fd99878bd06
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth
+description: Temporal.ZonedDateTime.prototype.toPlainYearMonth.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.toPlainYearMonth, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/name.js
new file mode 100644
index 00000000000..ac49eb0f40d
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth
+description: Temporal.ZonedDateTime.prototype.toPlainYearMonth.name is "toPlainYearMonth".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.toPlainYearMonth, "name", {
+ value: "toPlainYearMonth",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/not-a-constructor.js
new file mode 100644
index 00000000000..ec99dfc7d5b
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth
+description: >
+ Temporal.ZonedDateTime.prototype.toPlainYearMonth does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.toPlainYearMonth();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toPlainYearMonth), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.toPlainYearMonth)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/prop-desc.js
new file mode 100644
index 00000000000..3a331fa0115
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth
+description: The "toPlainYearMonth" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.toPlainYearMonth,
+ "function",
+ "`typeof ZonedDateTime.prototype.toPlainYearMonth` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "toPlainYearMonth", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..f87b615cfe4
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toPlainYearMonth());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..cbd2f29c05b
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toPlainYearMonth());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..4231ff9280e
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.toPlainYearMonth());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/balance-negative-time-units.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/balance-negative-time-units.js
new file mode 100644
index 00000000000..add1ee4b59d
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/balance-negative-time-units.js
@@ -0,0 +1,40 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-temporalzoneddatetimetostring step 9:
+ 9. Let _dateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _isoCalendar_).
+ sec-get-temporal.zoneddatetime.prototype.tostring step 9:
+ 9. Return ? TemporalZonedDateTimeToString(_zonedDateTime_, _precision_.[[Precision]], _showCalendar_, _showTimeZone_, _showOffset_, _precision_.[[Increment]], _precision_.[[Unit]], _roundingMode_).
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// ZonedDateTime
+const tz = new Temporal.TimeZone("-00:00:00.000000002");
+const datetime = new Temporal.ZonedDateTime(1001n, tz);
+
+const isoString = datetime.toString();
+
+assert.sameValue(isoString, "1970-01-01T00:00:00.000000999-00:00:00.000000002[-00:00:00.000000002]");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin.js
new file mode 100644
index 00000000000..416e5e26e3c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.toString
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.toString),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toString),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toString),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.toString.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-invalid-string.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-invalid-string.js
new file mode 100644
index 00000000000..0b4967d73cf
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-invalid-string.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.protoype.tostring
+description: RangeError thrown when calendarName option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « String », « *"auto"*, *"always"*, *"never"* », *"auto"*).
+ sec-temporal.zoneddatetime.protoype.tostring step 6:
+ 6. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_64_321n, "UTC");
+assert.throws(RangeError, () => datetime.toString({ calendarName: "other string" }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-undefined.js
new file mode 100644
index 00000000000..207d1df30f8
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-undefined.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.protoype.tostring
+description: Fallback value for calendarName option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « String », « *"auto"*, *"always"*, *"never"* », *"auto"*).
+ sec-temporal.zoneddatetime.protoype.tostring step 6:
+ 6. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+features: [Temporal]
+---*/
+
+const calendar = {
+ toString() { return "custom"; }
+};
+const datetime1 = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const datetime2 = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+
+[
+ [datetime1, "2001-09-09T01:46:40.987654321+00:00[UTC]"],
+ [datetime2, "2001-09-09T01:46:40.987654321+00:00[UTC][u-ca=custom]"],
+].forEach(([datetime, expected]) => {
+ const explicit = datetime.toString({ calendarName: undefined });
+ assert.sameValue(explicit, expected, "default calendarName option is auto");
+
+ // See options-undefined.js for {}
+});
+
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-wrong-type.js
new file mode 100644
index 00000000000..5f1e7e73a9a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-wrong-type.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.protoype.tostring
+description: Type conversions for calendarName option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-toshowcalendaroption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « String », « *"auto"*, *"always"*, *"never"* », *"auto"*).
+ sec-temporal.zoneddatetime.protoype.tostring step 6:
+ 6. Let _showCalendar_ be ? ToShowCalendarOption(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = {
+ toString() { return "custom"; }
+};
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+
+TemporalHelpers.checkStringOptionWrongType("calendarName", "auto",
+ (calendarName) => datetime.toString({ calendarName }),
+ (result, descr) => assert.sameValue(result, "2001-09-09T01:46:40.987654321+00:00[UTC][u-ca=custom]", descr),
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-invalid-string.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-invalid-string.js
new file mode 100644
index 00000000000..ea172ceb80e
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-invalid-string.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option not one of the allowed string values
+info: |
+ sec-getstringornumberoption step 4:
+ 4. If _stringValues_ is not *undefined* and _stringValues_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.instant.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToDurationSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_650_000n, "UTC");
+
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: "other string" }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-nan.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-nan.js
new file mode 100644
index 00000000000..88544be37fe
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-nan.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.zoneddatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_650_000n, "UTC");
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: NaN }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-non-integer.js
new file mode 100644
index 00000000000..93685aedece
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-non-integer.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: Rounding for fractionalSecondDigits option
+info: |
+ sec-getstringornumberoption step 3.b:
+ b. Return floor(ℝ(_value_)).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.zoneddatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToDurationSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_650_000n, "UTC");
+
+const string = datetime.toString({ fractionalSecondDigits: 2.5 });
+assert.sameValue(string, "2001-09-09T01:46:40.98+00:00[UTC]", "fractionalSecondDigits 2.5 floors to 2");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-out-of-range.js
new file mode 100644
index 00000000000..36ae8d14293
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-out-of-range.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: RangeError thrown when fractionalSecondDigits option out of range
+info: |
+ sec-getstringornumberoption step 3.a:
+ a. If _value_ < _minimum_ or _value_ > _maximum_, throw a *RangeError* exception.
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.zoneddatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToDurationSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_650_000n, "UTC");
+
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: -1 }));
+assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: 10 }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-undefined.js
new file mode 100644
index 00000000000..a4e0a10b5f0
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-undefined.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: Fallback value for fractionalSecondDigits option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, *"stringOrNumber"*, *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.zoneddatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToDurationSecondsStringPrecision(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_650_000n, "UTC");
+
+const explicit = datetime.toString({ fractionalSecondDigits: undefined });
+assert.sameValue(explicit, "2001-09-09T01:46:40.98765+00:00[UTC]", "default fractionalSecondDigits is auto");
+
+// See options-undefined.js for {}
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-wrong-type.js
new file mode 100644
index 00000000000..b147dbac3e2
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: Type conversions for fractionalSecondDigits option
+info: |
+ sec-getoption steps 8–9:
+ 8. Else if _type_ is Number, then
+ a. Set _value_ to ? ToNumber(value).
+ b. ...
+ 9. Else,
+ a. Set _value_ to ? ToString(value).
+ sec-getstringornumberoption step 2:
+ 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_).
+ sec-temporal-tosecondsstringprecision step 9:
+ 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*).
+ sec-temporal.zoneddatetime.prototype.tostring step 4:
+ 4. Let _precision_ be ? ToSecondsStringPrecision(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_650_000n, "UTC");
+TemporalHelpers.checkFractionalSecondDigitsOptionWrongType(datetime);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/length.js
new file mode 100644
index 00000000000..b47f8dde923
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: Temporal.ZonedDateTime.prototype.toString.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.toString, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/name.js
new file mode 100644
index 00000000000..f9c5888dffd
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: Temporal.ZonedDateTime.prototype.toString.name is "toString".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.toString, "name", {
+ value: "toString",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/negative-epochnanoseconds.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/negative-epochnanoseconds.js
new file mode 100644
index 00000000000..57dbb7951b8
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/negative-epochnanoseconds.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const result = datetime.toString();
+assert.sameValue(result, "1969-07-24T16:50:35.000000001+00:00[UTC]");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/not-a-constructor.js
new file mode 100644
index 00000000000..e7e75bc76b3
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: >
+ Temporal.ZonedDateTime.prototype.toString does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.toString();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toString), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.toString)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-invalid-string.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-invalid-string.js
new file mode 100644
index 00000000000..5842446a2f4
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-invalid-string.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.protoype.tostring
+description: RangeError thrown when offset option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-toshowoffsetoption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"auto"*, *"never"* », *"auto"*).
+ sec-temporal.zoneddatetime.protoype.tostring step 8:
+ 8. Let _showOffset_ be ? ToShowOffsetOption(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_64_321n, "UTC");
+assert.throws(RangeError, () => datetime.toString({ offset: "other string" }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-undefined.js
new file mode 100644
index 00000000000..dd81e25e62a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-undefined.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.protoype.tostring
+description: Fallback value for offset option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-toshowoffsetoption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"auto"*, *"never"* », *"auto"*).
+ sec-temporal.zoneddatetime.protoype.tostring step 8:
+ 8. Let _showOffset_ be ? ToShowOffsetOption(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+
+const explicit = datetime.toString({ offset: undefined });
+assert.sameValue(explicit, "2001-09-09T01:46:40.987654321+00:00[UTC]", "default offset option is auto");
+
+// See options-undefined.js for {}
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-wrong-type.js
new file mode 100644
index 00000000000..0c6a6f76911
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-wrong-type.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.protoype.tostring
+description: Type conversions for offset option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-toshowoffsetoption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"auto"*, *"never"* », *"auto"*).
+ sec-temporal.zoneddatetime.protoype.tostring step 8:
+ 8. Let _showOffset_ be ? ToShowOffsetOption(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+
+TemporalHelpers.checkStringOptionWrongType("offset", "auto",
+ (offset) => datetime.toString({ offset }),
+ (result, descr) => assert.sameValue(result, "2001-09-09T01:46:40.987654321+00:00[UTC]", descr),
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/options-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/options-undefined.js
new file mode 100644
index 00000000000..4d5280cbf00
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/options-undefined.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const calendar = {
+ toString() { return "custom"; }
+};
+const datetime1 = new Temporal.ZonedDateTime(957270896_987_650_000n, "UTC");
+const datetime2 = new Temporal.ZonedDateTime(957270896_987_650_000n, "UTC", calendar);
+
+[
+ [datetime1, "2000-05-02T12:34:56.98765+00:00[UTC]"],
+ [datetime2, "2000-05-02T12:34:56.98765+00:00[UTC][u-ca=custom]"],
+].forEach(([datetime, expected]) => {
+ const explicit = datetime.toString(undefined);
+ assert.sameValue(explicit, expected, "default show options are auto, precision is auto, and no rounding");
+
+ const propertyImplicit = datetime.toString({});
+ assert.sameValue(propertyImplicit, expected, "default show options are auto, precision is auto, and no rounding");
+
+ const implicit = datetime.toString();
+ assert.sameValue(implicit, expected, "default show options are auto, precision is auto, and no rounding");
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/prop-desc.js
new file mode 100644
index 00000000000..057028b6451
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: The "toString" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.toString,
+ "function",
+ "`typeof ZonedDateTime.prototype.toString` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "toString", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-invalid-string.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-invalid-string.js
new file mode 100644
index 00000000000..01d86acc415
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-invalid-string.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+assert.throws(RangeError, () => datetime.toString({ smallestUnit: "microsecond", roundingMode: "other string" }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-undefined.js
new file mode 100644
index 00000000000..752c9d6976a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: Fallback value for roundingMode option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+
+const explicit1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: undefined });
+assert.sameValue(explicit1, "2001-09-09T01:46:40.123987+00:00[UTC]", "default roundingMode is trunc");
+const implicit1 = datetime.toString({ smallestUnit: "microsecond" });
+assert.sameValue(implicit1, "2001-09-09T01:46:40.123987+00:00[UTC]", "default roundingMode is trunc");
+
+const explicit2 = datetime.toString({ smallestUnit: "millisecond", roundingMode: undefined });
+assert.sameValue(explicit2, "2001-09-09T01:46:40.123+00:00[UTC]", "default roundingMode is trunc");
+const implicit2 = datetime.toString({ smallestUnit: "millisecond" });
+assert.sameValue(implicit2, "2001-09-09T01:46:40.123+00:00[UTC]", "default roundingMode is trunc");
+
+const explicit3 = datetime.toString({ smallestUnit: "second", roundingMode: undefined });
+assert.sameValue(explicit3, "2001-09-09T01:46:40+00:00[UTC]", "default roundingMode is trunc");
+const implicit3 = datetime.toString({ smallestUnit: "second" });
+assert.sameValue(implicit3, "2001-09-09T01:46:40+00:00[UTC]", "default roundingMode is trunc");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-wrong-type.js
new file mode 100644
index 00000000000..784fac456bc
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-wrong-type.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => datetime.toString({ smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => assert.sameValue(result, "2001-09-09T01:46:40.123987+00:00[UTC]", descr),
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-invalid-string.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-invalid-string.js
new file mode 100644
index 00000000000..d3940464811
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-invalid-string.js
@@ -0,0 +1,11 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+assert.throws(RangeError, () => datetime.toString({ smallestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-plurals-accepted.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-plurals-accepted.js
new file mode 100644
index 00000000000..0c824f4c435
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-plurals-accepted.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_456_789n, "UTC");
+const validUnits = [
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => datetime.toString({ smallestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-undefined.js
new file mode 100644
index 00000000000..f3ef2656576
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-undefined.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: Fallback value for smallestUnit option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+
+const explicit1 = datetime.toString({ smallestUnit: undefined, fractionalSecondDigits: 6 });
+assert.sameValue(explicit1, "2001-09-09T01:46:40.123987+00:00[UTC]", "default smallestUnit defers to fractionalSecondDigits");
+const implicit1 = datetime.toString({ fractionalSecondDigits: 6 });
+assert.sameValue(implicit1, "2001-09-09T01:46:40.123987+00:00[UTC]", "default smallestUnit defers to fractionalSecondDigits");
+
+const explicit2 = datetime.toString({ smallestUnit: undefined, fractionalSecondDigits: 3 });
+assert.sameValue(explicit2, "2001-09-09T01:46:40.123+00:00[UTC]", "default smallestUnit defers to fractionalSecondDigits");
+const implicit2 = datetime.toString({ fractionalSecondDigits: 3 });
+assert.sameValue(implicit2, "2001-09-09T01:46:40.123+00:00[UTC]", "default smallestUnit defers to fractionalSecondDigits");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-valid-units.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-valid-units.js
new file mode 100644
index 00000000000..12c10368b64
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-valid-units.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: Valid units for the smallestUnit option
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_456_789n, "UTC");
+
+assert.sameValue(datetime.toString({ smallestUnit: "minute" }), "2001-09-09T01:46+00:00[UTC]");
+assert.sameValue(datetime.toString({ smallestUnit: "second" }), "2001-09-09T01:46:40+00:00[UTC]");
+assert.sameValue(datetime.toString({ smallestUnit: "millisecond" }), "2001-09-09T01:46:40.123+00:00[UTC]");
+assert.sameValue(datetime.toString({ smallestUnit: "microsecond" }), "2001-09-09T01:46:40.123456+00:00[UTC]");
+assert.sameValue(datetime.toString({ smallestUnit: "nanosecond" }), "2001-09-09T01:46:40.123456789+00:00[UTC]");
+
+const notValid = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+];
+
+notValid.forEach((smallestUnit) => {
+ assert.throws(RangeError, () => datetime.toString({ smallestUnit }), smallestUnit);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-wrong-type.js
new file mode 100644
index 00000000000..ef441654360
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-wrong-type.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => datetime.toString({ smallestUnit }),
+ (result, descr) => assert.sameValue(result, "2001-09-09T01:46:40.123987+00:00[UTC]", descr),
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..f32b4dccc0a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toString());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..f4325d7f362
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.toString());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..4d8dc3812b4
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.tostring
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.toString());
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-invalid-string.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-invalid-string.js
new file mode 100644
index 00000000000..94067c25900
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-invalid-string.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.protoype.tostring
+description: RangeError thrown when timeZoneName option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-toshowtimezonenameoption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"timeZoneName"*, « String », « *"auto"*, *"never"* », *"auto"*).
+ sec-temporal.zoneddatetime.protoype.tostring step 7:
+ 7. Let _showTimeZone_ be ? ToShowTimeZoneNameOption(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_64_321n, "UTC");
+assert.throws(RangeError, () => datetime.toString({ timeZoneName: "other string" }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-undefined.js
new file mode 100644
index 00000000000..7973c987d74
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-undefined.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.protoype.tostring
+description: Fallback value for timeZoneName option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-toshowtimezonenameoption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"timeZoneName"*, « String », « *"auto"*, *"never"* », *"auto"*).
+ sec-temporal.zoneddatetime.protoype.tostring step 7:
+ 7. Let _showTimeZone_ be ? ToShowTimeZoneNameOption(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+
+const explicit = datetime.toString({ timeZoneName: undefined });
+assert.sameValue(explicit, "2001-09-09T01:46:40.987654321+00:00[UTC]", "default timeZoneName option is auto");
+
+// See options-undefined.js for {}
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-wrong-type.js
new file mode 100644
index 00000000000..e83112b28dd
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-wrong-type.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.protoype.tostring
+description: Type conversions for timeZoneName option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-toshowtimezonenameoption step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"timeZoneName"*, « String », « *"auto"*, *"never"* », *"auto"*).
+ sec-temporal.zoneddatetime.protoype.tostring step 7:
+ 7. Let _showTimeZone_ be ? ToShowTimeZoneNameOption(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+
+TemporalHelpers.checkStringOptionWrongType("timeZoneName", "auto",
+ (timeZoneName) => datetime.toString({ timeZoneName }),
+ (result, descr) => assert.sameValue(result, "2001-09-09T01:46:40.987654321+00:00[UTC]", descr),
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/balance-negative-time-units.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/balance-negative-time-units.js
new file mode 100644
index 00000000000..2005406753c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/balance-negative-time-units.js
@@ -0,0 +1,54 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-differencetime step 8:
+ 8. Let _bt_ be ? BalanceTime(_hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal-differenceisodatetime step 2:
+ 2. Let _timeDifference_ be ? DifferenceTime(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_).
+ sec-temporal-differencezoneddatetime step 7:
+ 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_).
+ sec-temporal.zoneddatetime.prototype.until step 15:
+ 15. Let _difference_ be ? DifferenceZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _other_.[[Nanoseconds]], _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]], _largestUnit_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("UTC");
+const datetime = new Temporal.ZonedDateTime(830998861_001_001_001n, timeZone);
+const options = { largestUnit: "days" };
+
+const result1 = new Temporal.ZonedDateTime(830995200_000_000_002n, timeZone).until(datetime, options);
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = new Temporal.ZonedDateTime(830995200_000_002_000n, timeZone).until(datetime, options);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = new Temporal.ZonedDateTime(830995200_002_000_000n, timeZone).until(datetime, options);
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = new Temporal.ZonedDateTime(830995202_000_000_000n, timeZone).until(datetime, options);
+TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = new Temporal.ZonedDateTime(830995320_000_000_000n, timeZone).until(datetime, options);
+TemporalHelpers.assertDuration(result5, 0, 0, 0, 0, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+// This one is different because hours are later balanced again in BalanceDuration
+const result6 = new Temporal.ZonedDateTime(831002400_000_000_000n, timeZone).until(datetime, options);
+TemporalHelpers.assertDuration(result6, 0, 0, 0, 0, 0, -58, -58, -998, -998, -999, "hours balance");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/builtin.js
new file mode 100644
index 00000000000..790fa26fd30
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.until
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.until),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.until),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.until),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.until.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-copy-of-options.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-copy-of-options.js
new file mode 100644
index 00000000000..5bc1242b13a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-copy-of-options.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: The dateUntil() method on the calendar is called with a copy of the options bag
+features: [Temporal]
+---*/
+
+const originalOptions = {
+ largestUnit: "year",
+ shouldBeCopied: {},
+};
+let called = false;
+
+class Calendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateUntil(d1, d2, options) {
+ called = true;
+ assert.notSameValue(options, originalOptions, "options bag should be a copy");
+ assert.sameValue(options.shouldBeCopied, originalOptions.shouldBeCopied, "options bag should be a shallow copy");
+ return new Temporal.Duration(1);
+ }
+}
+const calendar = new Calendar();
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+// exactly one year later; avoids NanosecondsToDays path
+const later = new Temporal.ZonedDateTime(1_031_536_000_000_000_000n, "UTC", calendar);
+earlier.until(later, originalOptions);
+assert(called, "calendar.dateUntil must be called");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js
new file mode 100644
index 00000000000..c07900f71bd
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js
@@ -0,0 +1,114 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
+info: |
+ sec-temporal.zoneddatetime.prototype.until steps 13–17:
+ 13. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ c. Return ...
+ 14. ...
+ 15. Let _difference_ be ? DifferenceZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _other_.[[Nanoseconds]], _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]], _largestUnit_).
+ 16. Let _roundResult_ be ? RoundDuration(_difference_.[[Years]], _difference_.[[Months]], _difference_.[[Weeks]], _difference_.[[Days]], _difference_.[[Hours]], _difference_.[[Minutes]], _difference_.[[Seconds]], _difference_.[[Milliseconds]], _difference_.[[Microseconds]], _difference_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _zonedDateTime_).
+ 17. Let _result_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _zonedDateTime_).
+ sec-temporal-differencezoneddatetime steps 7 and 11:
+ 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_).
+ 11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_).
+ sec-temporal-roundduration steps 5.d and 8.n–p:
+ 5. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
+ ...
+ 8. If _unit_ is *"year"*, then
+ ...
+ n. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
+ o. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"year"*).
+ p. Let _timePassed_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _daysLater_, _untilOptions_)
+ sec-temporal-adjustroundeddurationdays steps 1 and 9:
+ 1. If _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot; or _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*; or _unit_ is *"nanosecond"* and _increment_ is 1, then
+ a. Return ...
+ ...
+ 9. Let _adjustedDateDuration_ be ? AddDuration(_years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0, 0, 0, 0, _direction_, 0, 0, 0, 0, 0, 0, _relativeTo_).
+ sec-temporal-addduration step 7.a–g:
+ a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot.
+ ...
+ f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then
+ ...
+ g. Else,
+ i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
+ sec-temporal-nanosecondstodays step 11:
+ 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*).
+ sec-temporal-differenceisodatetime steps 9–11:
+ 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
+ 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
+ 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+ const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC", calendar);
+ earlier.until(later, { largestUnit });
+ },
+ {
+ years: ["year", "day"],
+ months: ["month", "day"],
+ weeks: ["week", "day"],
+ days: ["day", "day"],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+// Additionally check the path that goes through AdjustRoundedDurationDays
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, largestUnit) => {
+ const earlier = new Temporal.ZonedDateTime(0n, "UTC", calendar);
+ const later = new Temporal.ZonedDateTime(86_399_999_999_999n, "UTC", calendar);
+ earlier.until(later, { largestUnit, roundingIncrement: 2, roundingMode: 'ceil' });
+ },
+ {
+ years: ["year", "day", "day", "day"],
+ months: ["month", "day", "day", "day"],
+ weeks: ["week", "day", "day", "day"],
+ days: ["day", "day", "day", "day"],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
+
+// Also check the path that goes through RoundDuration when smallestUnit is
+// given
+
+TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
+ (calendar, smallestUnit) => {
+ const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+ const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC", calendar);
+ earlier.until(later, { smallestUnit });
+ },
+ {
+ years: ["year", "day", "day", "year"],
+ months: ["month", "day", "day"],
+ weeks: ["week", "day", "day"],
+ days: ["day", "day", "day"],
+ hours: [],
+ minutes: [],
+ seconds: [],
+ milliseconds: [],
+ microseconds: [],
+ nanoseconds: []
+ }
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-fields-iterable.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-fields-iterable.js
new file mode 100644
index 00000000000..f9ca14e1931
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-fields-iterable.js
@@ -0,0 +1,39 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.zoneddatetime.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "hour",
+ "microsecond",
+ "millisecond",
+ "minute",
+ "month",
+ "monthCode",
+ "nanosecond",
+ "second",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+datetime.until({ year: 2005, month: 6, day: 2, timeZone: "UTC", calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-temporal-object.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-temporal-object.js
new file mode 100644
index 00000000000..d7ddb80b70d
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-temporal-object.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.zoneddatetime.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 2.b:
+ b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", temporalObject);
+ datetime.until({ year: 2005, month: 6, day: 2, timeZone: "UTC", calendar: temporalObject });
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/infinity-throws-rangeerror.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..6bb4a50be4c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.zoneddatetime.prototype.until
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.until({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.until({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-invalid-string.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-invalid-string.js
new file mode 100644
index 00000000000..77765bdfc8c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+assert.throws(RangeError, () => earlier.until(later, { largestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-plurals-accepted.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-plurals-accepted.js
new file mode 100644
index 00000000000..0b04af5b6c4
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-plurals-accepted.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC");
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => earlier.until(later, { largestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-undefined.js
new file mode 100644
index 00000000000..84619cbab91
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+
+const explicit = earlier.until(later, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default largestUnit is hour");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default largestUnit is hour");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-wrong-type.js
new file mode 100644
index 00000000000..b102d73e8ef
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "year",
+ (largestUnit) => earlier.until(later, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 1, 1, 1, 1, 987, 654, 321, descr),
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/length.js
new file mode 100644
index 00000000000..a1e4337c320
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: Temporal.ZonedDateTime.prototype.until.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.until, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/name.js
new file mode 100644
index 00000000000..88a70c92176
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: Temporal.ZonedDateTime.prototype.until.name is "until".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.until, "name", {
+ value: "until",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/negative-epochnanoseconds.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/negative-epochnanoseconds.js
new file mode 100644
index 00000000000..c9b818164df
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/negative-epochnanoseconds.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const result = datetime.until(new Temporal.ZonedDateTime(0n, "UTC"), { largestUnit: "month" });
+TemporalHelpers.assertDuration(result, 0, 5, 0, 7, 7, 9, 24, 999, 999, 999);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/not-a-constructor.js
new file mode 100644
index 00000000000..54bfbbe6bd0
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: >
+ Temporal.ZonedDateTime.prototype.until does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.until();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.until), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.until)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/options-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/options-undefined.js
new file mode 100644
index 00000000000..a85c673b2b1
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/options-undefined.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: Verify that undefined options are handled correctly.
+features: [BigInt, Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(957270896_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(959949296_987_654_322n, "UTC");
+
+const explicit = earlier.until(later, undefined);
+assert.sameValue(explicit.years, 0, "default largest unit is hours");
+assert.sameValue(explicit.months, 0, "default largest unit is hours");
+assert.sameValue(explicit.weeks, 0, "default largest unit is hours");
+assert.sameValue(explicit.days, 0, "default largest unit is hours");
+assert.sameValue(explicit.hours, 744, "default largest unit is hours");
+assert.sameValue(explicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
+
+const implicit = earlier.until(later);
+assert.sameValue(implicit.years, 0, "default largest unit is hours");
+assert.sameValue(implicit.months, 0, "default largest unit is hours");
+assert.sameValue(implicit.weeks, 0, "default largest unit is hours");
+assert.sameValue(implicit.days, 0, "default largest unit is hours");
+assert.sameValue(implicit.hours, 744, "default largest unit is hours");
+assert.sameValue(implicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/prop-desc.js
new file mode 100644
index 00000000000..04f2f59c23f
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: The "until" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.until,
+ "function",
+ "`typeof ZonedDateTime.prototype.until` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "until", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/read-time-fields-before-datefromfields.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/read-time-fields-before-datefromfields.js
new file mode 100644
index 00000000000..5a7001a01c7
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/read-time-fields-before-datefromfields.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.zoneddatetime.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 2.e:
+ e. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+const duration = datetime.until({ year: 2001, month: 9, day: 9, timeZone: "UTC", calendar });
+
+TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, -1, -46, -40, -987, -654, -321);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-nan.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-nan.js
new file mode 100644
index 00000000000..88e18fa42db
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-nan.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.zoneddatetime.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_988_655_322n, "UTC");
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: NaN }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-non-integer.js
new file mode 100644
index 00000000000..7302c429553
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-non-integer.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: Rounding for roundingIncrement option
+info: |
+ sec-temporal-totemporalroundingincrement step 7:
+ 7. Set _increment_ to floor(ℝ(_increment_)).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_000_000_000_000_005n, "UTC");
+const result = earlier.until(later, { roundingIncrement: 2.5 });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, "roundingIncrement 2.5 floors to 2");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-out-of-range.js
new file mode 100644
index 00000000000..d47d2f76ec4
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-out-of-range.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ sec-temporal-totemporalroundingincrement step 6:
+ 6. If _increment_ < 1 or _increment_ > _maximum_, throw a *RangeError* exception.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_000_000_000_000_005n, "UTC");
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: Infinity }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-undefined.js
new file mode 100644
index 00000000000..6684ddef2a5
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-undefined.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.zoneddatetime.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_988_655_322n, "UTC");
+
+const explicit = earlier.until(later, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 25, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 25, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-wrong-type.js
new file mode 100644
index 00000000000..f3794af5719
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.zoneddatetime.prototype.until step 12:
+ 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_988_655_322n, "UTC");
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => earlier.until(later, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 1, 1, 1, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 1, 1, 0, descr),
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-invalid-string.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-invalid-string.js
new file mode 100644
index 00000000000..70d98276591
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_123_987_500n, "UTC");
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "microsecond", roundingMode: "other string" }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-undefined.js
new file mode 100644
index 00000000000..b39c4729828
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-undefined.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_123_987_500n, "UTC");
+
+const explicit1 = earlier.until(later, { smallestUnit: "microsecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 0, 25, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+const implicit1 = earlier.until(later, { smallestUnit: "microsecond" });
+TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 0, 25, 1, 1, 123, 987, 0, "default roundingMode is trunc");
+
+const explicit2 = earlier.until(later, { smallestUnit: "millisecond", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 0, 25, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+const implicit2 = earlier.until(later, { smallestUnit: "millisecond" });
+TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 0, 25, 1, 1, 123, 0, 0, "default roundingMode is trunc");
+
+const explicit3 = earlier.until(later, { smallestUnit: "second", roundingMode: undefined });
+TemporalHelpers.assertDuration(explicit3, 0, 0, 0, 0, 25, 1, 1, 0, 0, 0, "default roundingMode is trunc");
+const implicit3 = earlier.until(later, { smallestUnit: "second" });
+TemporalHelpers.assertDuration(implicit3, 0, 0, 0, 0, 25, 1, 1, 0, 0, 0, "default roundingMode is trunc");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-wrong-type.js
new file mode 100644
index 00000000000..49865d78e28
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_123_987_500n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => earlier.until(later, { smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 123, 987, 0, descr),
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-invalid-string.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-invalid-string.js
new file mode 100644
index 00000000000..b5a2f80114b
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-invalid-string.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "other string" }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-plurals-accepted.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-plurals-accepted.js
new file mode 100644
index 00000000000..3d974f993fc
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-plurals-accepted.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC");
+const validUnits = [
+ "year",
+ "month",
+ "week",
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => earlier.until(later, { smallestUnit }), validUnits);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-undefined.js
new file mode 100644
index 00000000000..f1496a3253f
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+
+const explicit = earlier.until(later, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+const implicit = earlier.until(later, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-wrong-type.js
new file mode 100644
index 00000000000..30e3d004513
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-wrong-type.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => earlier.until(later, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 987, 654, 0, descr),
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..5a7f0527caf
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const other = new Temporal.ZonedDateTime(1_100_000_000_123_456_789n, timeZone);
+ assert.throws(RangeError, () => datetime.until(other, { largestUnit: "days" }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..81d031c5488
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_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 other = new Temporal.ZonedDateTime(1_100_000_000_123_456_789n, timeZone);
+ assert.throws(RangeError, () => datetime.until(other, { largestUnit: "days" }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..fd51309e238
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const other = new Temporal.ZonedDateTime(1_100_000_000_123_456_789n, timeZone);
+ assert.throws(TypeError, () => datetime.until(other, { largestUnit: "days" }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getpossibleinstantsfor-iterable.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 00000000000..bed8dcd2be8
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,38 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.zoneddatetime.prototype.until step 3:
+ 3. Set _other_ to ? ToTemporalZonedDateTime(_other_).
+ sec-temporal-totemporalzoneddatetime step 7:
+ 7. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// Not called on the instance's time zone
+
+const expected1 = [];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone);
+ datetime.until({ year: 2005, month: 6, day: 2, timeZone: "UTC" });
+}, expected1);
+
+// Called on the argument's time zone
+
+const expected2 = [
+ "2005-06-02T00:00:00",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+ datetime.until({ year: 2005, month: 6, day: 2, timeZone });
+}, expected2);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-string-datetime.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-string-datetime.js
new file mode 100644
index 00000000000..61169219006
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-string-datetime.js
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let expectedTimeZone = "UTC";
+const instance1 = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance1.until({ year: 2020, month: 5, day: 2, timeZone }), "bare date-time string is not a time zone");
+assert.throws(RangeError, () => instance1.until({ year: 2020, month: 5, day: 2, timeZone: { timeZone } }), "bare date-time string is not a time zone");
+
+// The following are all valid strings so should not throw. They should produce
+// expectedTimeZone, so additionally the operation will not throw due to the
+// time zones being different on the receiver and the argument.
+
+timeZone = "2021-08-19T17:30Z";
+instance1.until({ year: 2020, month: 5, day: 2, timeZone });
+instance1.until({ year: 2020, month: 5, day: 2, timeZone: { timeZone } });
+
+expectedTimeZone = "-07:00";
+const instance2 = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+timeZone = "2021-08-19T17:30-07:00";
+instance2.until({ year: 2020, month: 5, day: 2, timeZone });
+instance2.until({ year: 2020, month: 5, day: 2, timeZone: { timeZone } });
+
+expectedTimeZone = "America/Vancouver";
+const instance3 = new Temporal.ZonedDateTime(0n, expectedTimeZone);
+timeZone = "2021-08-19T17:30[America/Vancouver]";
+instance3.until({ year: 2020, month: 5, day: 2, timeZone });
+instance3.until({ year: 2020, month: 5, day: 2, timeZone: { timeZone } });
+
+timeZone = "2021-08-19T17:30Z[America/Vancouver]";
+instance3.until({ year: 2020, month: 5, day: 2, timeZone });
+instance3.until({ year: 2020, month: 5, day: 2, timeZone: { timeZone } });
+
+timeZone = "2021-08-19T17:30-07:00[America/Vancouver]";
+instance3.until({ year: 2020, month: 5, day: 2, timeZone });
+instance3.until({ year: 2020, month: 5, day: 2, timeZone: { timeZone } });
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/zoneddatetime-string.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/zoneddatetime-string.js
new file mode 100644
index 00000000000..1b4b5cc3389
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/zoneddatetime-string.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.until
+description: Conversion of ISO date-time strings to Temporal.ZonedDateTime instances
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.until(str), "bare date-time string is not a ZonedDateTime");
+str = "1970-01-01T00:00Z";
+assert.throws(RangeError, () => instance.until(str), "date-time + Z is not a ZonedDateTime");
+str = "1970-01-01T00:00+01:00";
+assert.throws(RangeError, () => instance.until(str), "date-time + offset is not a ZonedDateTime");
+
+str = "1970-01-01T00:00[Europe/Berlin]";
+const result1 = instance.until(str);
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, "date-time + IANA annotation preserves wall time in the time zone");
+
+str = "1970-01-01T00:00Z[Europe/Berlin]";
+const result2 = instance.until(str);
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation preserves exact time in the time zone");
+
+str = "1970-01-01T00:00+01:00[Europe/Berlin]";
+const result3 = instance.until(str);
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, "date-time + offset + IANA annotation ensures both exact and wall time match");
+
+str = "1970-01-01T00:00-04:15[Europe/Berlin]";
+assert.throws(RangeError, () => instance.until(str), "date-time + offset + IANA annotation throws if wall time and exact time mismatch");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/valueOf/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/valueOf/builtin.js
new file mode 100644
index 00000000000..136675925fc
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/valueOf/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.valueof
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.valueOf
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.valueOf),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.valueOf),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.valueOf),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.valueOf.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/valueOf/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/valueOf/length.js
new file mode 100644
index 00000000000..229be812515
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/valueOf/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.valueof
+description: Temporal.ZonedDateTime.prototype.valueOf.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.valueOf, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/valueOf/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/valueOf/name.js
new file mode 100644
index 00000000000..094dd08288e
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/valueOf/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.valueof
+description: Temporal.ZonedDateTime.prototype.valueOf.name is "valueOf".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.valueOf, "name", {
+ value: "valueOf",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/valueOf/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/valueOf/not-a-constructor.js
new file mode 100644
index 00000000000..47ca09e79e1
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/valueOf/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.valueof
+description: >
+ Temporal.ZonedDateTime.prototype.valueOf does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.valueOf();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.valueOf), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.valueOf)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/valueOf/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/valueOf/prop-desc.js
new file mode 100644
index 00000000000..27a3a0851b2
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/valueOf/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.valueof
+description: The "valueOf" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.valueOf,
+ "function",
+ "`typeof ZonedDateTime.prototype.valueOf` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "valueOf", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/prop-desc.js
new file mode 100644
index 00000000000..a0187375915
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.weekofyear
+description: The "weekOfYear" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "weekOfYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..da2d1a9c024
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.weekofyear
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.weekOfYear);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..ceb935001fb
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.weekofyear
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.weekOfYear);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..cef077319eb
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.weekofyear
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.weekOfYear);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/balance-negative-time-units.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/balance-negative-time-units.js
new file mode 100644
index 00000000000..7903a25e68f
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/balance-negative-time-units.js
@@ -0,0 +1,62 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-addtime step 8:
+ 8. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-adddatetime step 1:
+ 1. Let _timeResult_ be ? AddTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal-builtintimezonegetinstantfor step 13.a:
+ a. Let _earlier_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], 0, 0, 0, 0, 0, 0, 0, 0, 0, −_nanoseconds_, *"constrain"*).
+ sec-temporal-interpretisodatetimeoffset steps 4–10:
+ 4. If _offsetNanoseconds_ is *null*, or _offset_ is *"ignore"*, then
+ a. Let _instant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _dateTime_, _disambiguation_).
+ ...
+ ...
+ 6. Assert: _offset_ is *"prefer"* or *"reject"*.
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ ...
+ 9. If _offset_ is *"reject"*, throw a *RangeError* exception.
+ 10. Let _instant_ be ? DisambiguatePossibleInstants(_possibleInstants_, _timeZone_, _dateTime_, _disambiguation_).
+ sec-temporal.zoneddatetime.prototype.with step 26:
+ 26. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_dateTimeResult_.[[Year]], _dateTimeResult_.[[Month]], _dateTimeResult_.[[Day]], _dateTimeResult_.[[Hour]], _dateTimeResult_.[[Minute]], _dateTimeResult_.[[Second]], _dateTimeResult_.[[Millisecond]], _dateTimeResult_.[[Microsecond]], _dateTimeResult_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const shiftInstant = new Temporal.Instant(3661_001_001_001n);
+const tz1 = TemporalHelpers.oneShiftTimeZone(shiftInstant, 2);
+const datetime1 = new Temporal.ZonedDateTime(3661_001_001_000n, tz1);
+
+// This code path is encountered if offset is `ignore` or `prefer`,
+// disambiguation is `earlier` and the shift is a spring-forward change
+datetime1.with({ nanosecond: 1 }, { offset: "ignore", disambiguation: "earlier" });
+
+const expected = [
+ "1970-01-01T01:01:01.001001001",
+ "1970-01-01T01:01:01.001000999",
+];
+assert.compareArray(tz1.getPossibleInstantsForCalledWith, expected);
+
+const tz2 = TemporalHelpers.oneShiftTimeZone(shiftInstant, 2);
+const datetime2 = new Temporal.ZonedDateTime(3661_001_001_000n, tz2);
+
+datetime2.with({ nanosecond: 1 }, { offset: "prefer", disambiguation: "earlier" });
+
+assert.compareArray(tz2.getPossibleInstantsForCalledWith, expected);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/builtin.js
new file mode 100644
index 00000000000..2b11cd1eec2
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.with
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.with),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.with),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.with),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.with.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-fields-iterable.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-fields-iterable.js
new file mode 100644
index 00000000000..326a7b8d016
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-fields-iterable.js
@@ -0,0 +1,35 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.zoneddatetime.prototype.with step 9:
+ 9. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "hour",
+ "microsecond",
+ "millisecond",
+ "minute",
+ "month",
+ "monthCode",
+ "nanosecond",
+ "second",
+ "year",
+];
+
+const calendar = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+datetime.with({ year: 2005 });
+
+assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-merge-fields-returns-primitive.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-merge-fields-returns-primitive.js
new file mode 100644
index 00000000000..c6c2879ea3c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-merge-fields-returns-primitive.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: >
+ with() should throw a TypeError if mergeFields() returns a primitive,
+ without passing the value on to any other calendar methods
+includes: [compareArray.js, temporalHelpers.js]
+features: [BigInt, Symbol, Temporal]
+---*/
+
+[undefined, null, true, 3.14159, "bad value", Symbol("no"), 7n].forEach((primitive) => {
+ const calendar = TemporalHelpers.calendarMergeFieldsReturnsPrimitive(primitive);
+ const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+ assert.throws(TypeError, () => instance.with({ year: 2005 }), "bad return from mergeFields() throws");
+ assert.sameValue(calendar.dateFromFieldsCallCount, 0, "dateFromFields() never called");
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/copies-merge-fields-object.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/copies-merge-fields-object.js
new file mode 100644
index 00000000000..b62f3f98b87
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/copies-merge-fields-object.js
@@ -0,0 +1,59 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: The object returned from mergeFields() is copied before being passed to dateFromFields().
+info: |
+ sec-temporal.plaindatetime.prototype.with steps 18–19 and 23:
+ 18. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialDate_).
+ 19. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, « *"timeZone"* »).
+ 23. Let _dateTimeResult_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields step 2:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "get day",
+ "get day.valueOf",
+ "call day.valueOf",
+ "get hour",
+ "get hour.valueOf",
+ "call hour.valueOf",
+ "get microsecond",
+ "get microsecond.valueOf",
+ "call microsecond.valueOf",
+ "get millisecond",
+ "get millisecond.valueOf",
+ "call millisecond.valueOf",
+ "get minute",
+ "get minute.valueOf",
+ "call minute.valueOf",
+ "get month",
+ "get month.valueOf",
+ "call month.valueOf",
+ "get monthCode",
+ "get monthCode.toString",
+ "call monthCode.toString",
+ "get nanosecond",
+ "get nanosecond.valueOf",
+ "call nanosecond.valueOf",
+ "get second",
+ "get second.valueOf",
+ "call second.valueOf",
+ "get year",
+ "get year.valueOf",
+ "call year.valueOf",
+ "get offset",
+ "get offset.toString",
+ "call offset.toString",
+ "get timeZone",
+];
+
+const calendar = TemporalHelpers.calendarMergeFieldsGetters();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+datetime.with({ year: 2022 });
+
+assert.compareArray(calendar.mergeFieldsReturnOperations, expected, "getters called on mergeFields return");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-invalid-string.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-invalid-string.js
new file mode 100644
index 00000000000..06d6bd1fa07
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-invalid-string.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.protoype.with
+description: RangeError thrown when disambiguation option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal.zoneddatetime.protoype.with step 14:
+ 14. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+assert.throws(RangeError, () => datetime.with({ hour: 2 }, { disambiguation: "other string" }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-undefined.js
new file mode 100644
index 00000000000..028c38dbf5c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-undefined.js
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: Fallback value for disambiguation option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal.zoneddatetime.protoype.with step 14:
+ 14. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const timeZone = TemporalHelpers.springForwardFallBackTimeZone();
+const springForwardDatetime = new Temporal.ZonedDateTime(954702001_000_000_000n, timeZone);
+const fallBackDatetime = new Temporal.ZonedDateTime(972849601_000_000_000n, timeZone);
+const offset = "ignore";
+
+[
+ [springForwardDatetime, { hour: 2, minute: 30 }, 954671401_000_000_000n],
+ [fallBackDatetime, { hour: 1, minute: 30 }, 972808201_000_000_000n],
+].forEach(([datetime, fields, expected]) => {
+ const explicit = datetime.with(fields, { offset, disambiguation: undefined });
+ assert.sameValue(explicit.epochNanoseconds, expected, "default disambiguation is compatible");
+ const implicit = datetime.with(fields, { offset });
+ assert.sameValue(implicit.epochNanoseconds, expected, "default disambiguation is compatible");
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-wrong-type.js
new file mode 100644
index 00000000000..d949dd82043
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-wrong-type.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: Type conversions for disambiguation option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaldisambiguation step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
+ sec-temporal.zoneddatetime.protoype.with step 14:
+ 14. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("disambiguation", "compatible",
+ (disambiguation) => datetime.with({ hour: 2 }, { disambiguation }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_003_600_987_654_321n, descr),
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/infinity-throws-rangeerror.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..7619bfb0af3
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/infinity-throws-rangeerror.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.zoneddatetime.prototype.with
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.with({ [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.with({ [prop]: obj }, { overflow }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+ });
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/length.js
new file mode 100644
index 00000000000..fb7eae0f8a0
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: Temporal.ZonedDateTime.prototype.with.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.with, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/name.js
new file mode 100644
index 00000000000..5ba3bbed236
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: Temporal.ZonedDateTime.prototype.with.name is "with".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.with, "name", {
+ value: "with",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/not-a-constructor.js
new file mode 100644
index 00000000000..33ac8f4ca7d
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: >
+ Temporal.ZonedDateTime.prototype.with does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.with();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.with), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.with)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/offset-invalid-string.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/offset-invalid-string.js
new file mode 100644
index 00000000000..2b870ec6ee2
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/offset-invalid-string.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.protoype.with
+description: RangeError thrown when offset option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloffset step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"prefer"*, *"use"*, *"ignore"*, *"reject"* », _fallback_).
+ sec-temporal.zoneddatetime.protoype.with step 15:
+ 15. Let _offset_ be ? ToTemporalOffset(_options_, *"prefer"*).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+assert.throws(RangeError, () => datetime.with({ hour: 2 }, { offset: "other string" }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/offset-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/offset-undefined.js
new file mode 100644
index 00000000000..a4ca0f75cf4
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/offset-undefined.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: Fallback value for offset option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloffset step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"prefer"*, *"use"*, *"ignore"*, *"reject"* », _fallback_).
+ sec-temporal.zoneddatetime.protoype.with step 15:
+ 15. Let _offset_ be ? ToTemporalOffset(_options_, *"prefer"*).
+features: [Temporal]
+---*/
+
+const timeZone = new Temporal.TimeZone("America/St_Johns");
+
+const datetime = new Temporal.ZonedDateTime(1572757201_000_000_000n, timeZone);
+const explicit = datetime.with({ minute: 31 }, { offset: undefined });
+assert.sameValue(explicit.epochNanoseconds, 1572757261_000_000_000n, "default offset is prefer");
+const implicit = datetime.with({ minute: 31 }, {});
+assert.sameValue(implicit.epochNanoseconds, 1572757261_000_000_000n, "default offset is prefer");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/offset-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/offset-wrong-type.js
new file mode 100644
index 00000000000..2b3a527d550
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/offset-wrong-type.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: Type conversions for offset option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloffset step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"prefer"*, *"use"*, *"ignore"*, *"reject"* », _fallback_).
+ sec-temporal.zoneddatetime.protoype.with step 15:
+ 15. Let _offset_ be ? ToTemporalOffset(_options_, *"prefer"*).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+TemporalHelpers.checkStringOptionWrongType("offset", "prefer",
+ (offset) => datetime.with({ hour: 2 }, { offset }),
+ (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_003_600_987_654_321n, descr),
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/options-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/options-undefined.js
new file mode 100644
index 00000000000..63d6f06867a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/options-undefined.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: Verify that undefined options are handled correctly.
+features: [BigInt, Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(949494896_987_654_321n, "UTC");
+const fields = { day: 31 };
+
+const explicit = datetime.with(fields, undefined);
+assert.sameValue(explicit.month, 2, "default overflow is constrain");
+assert.sameValue(explicit.day, 29, "default overflow is constrain");
+
+const implicit = datetime.with(fields);
+assert.sameValue(implicit.month, 2, "default overflow is constrain");
+assert.sameValue(implicit.day, 29, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-invalid-string.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-invalid-string.js
new file mode 100644
index 00000000000..96d5ab3799d
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-invalid-string.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: RangeError thrown when overflow option not one of the allowed string values
+info: |
+ sec-getoption step 10:
+ 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.zoneddatetime.prototype.with step 24:
+ 24. Let _dateTimeResult_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: "other string" }));
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-undefined.js
new file mode 100644
index 00000000000..b3c630c5378
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-undefined.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: Fallback value for overflow option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.zoneddatetime.prototype.with step 24:
+ 24. Let _dateTimeResult_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+const explicit = datetime.with({ second: 67 }, { overflow: undefined });
+assert.sameValue(explicit.epochNanoseconds, 1_000_000_019_987_654_321n, "default overflow is constrain");
+const implicit = datetime.with({ second: 67 }, {});
+assert.sameValue(implicit.epochNanoseconds, 1_000_000_019_987_654_321n, "default overflow is constrain");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-wrong-type.js
new file mode 100644
index 00000000000..77f3c9c6566
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-wrong-type.js
@@ -0,0 +1,46 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.with
+description: Type conversions for overflow option
+info: |
+ sec-getoption step 9.a:
+ a. Set _value_ to ? ToString(_value_).
+ sec-temporal-totemporaloverflow step 1:
+ 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*).
+ sec-temporal-interprettemporaldatetimefields steps 2–3:
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+ 3. Let _overflow_ be ? ToTemporalOverflow(_options_).
+ sec-temporal.zoneddatetime.prototype.with step 24:
+ 24. Let _dateTimeResult_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+
+// See TemporalHelpers.checkStringOptionWrongType(); this code path has
+// different expectations for observable calls
+
+assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: null }), "null");
+assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: true }), "true");
+assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: false }), "false");
+assert.throws(TypeError, () => datetime.with({ second: 41 }, { overflow: Symbol() }), "symbol");
+assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: 2n }), "bigint");
+assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: {} }), "plain object");
+
+// toString property is read once by Calendar.dateFromFields() in the builtin
+// calendars, to get the option value for the date part, and then once again
+// internally to get the option value for the time part.
+const expected = [
+ "get overflow.toString",
+ "call overflow.toString",
+ "get overflow.toString",
+ "call overflow.toString",
+];
+const actual = [];
+const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");
+const result = datetime.with({ second: 41 }, { overflow: observer });
+assert.sameValue(result.epochNanoseconds, 1_000_000_001_987_654_321n, "object with toString");
+assert.compareArray(actual, expected, "order of operations");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/prop-desc.js
new file mode 100644
index 00000000000..0774b88ad47
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: The "with" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.with,
+ "function",
+ "`typeof ZonedDateTime.prototype.with` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "with", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/read-time-fields-before-datefromfields.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/read-time-fields-before-datefromfields.js
new file mode 100644
index 00000000000..9f20ae7fa11
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/read-time-fields-before-datefromfields.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: The time fields are read from the object before being passed to dateFromFields().
+info: |
+ sec-temporal.zoneddatetime.prototype.with step 23:
+ 23. Let _dateTimeResult_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
+ sec-temporal-interprettemporaldatetimefields steps 1–2:
+ 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
+ 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarMakeInfinityTime();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
+const newDatetime = datetime.with({ year: 2022 });
+
+assert.sameValue(newDatetime.hour, 1, "hour value");
+assert.sameValue(newDatetime.minute, 46, "minute value");
+assert.sameValue(newDatetime.second, 40, "second value");
+assert.sameValue(newDatetime.millisecond, 987, "millisecond value");
+assert.sameValue(newDatetime.microsecond, 654, "microsecond value");
+assert.sameValue(newDatetime.nanosecond, 321, "nanosecond value");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/subclassing-ignored.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/subclassing-ignored.js
new file mode 100644
index 00000000000..5fb3f6f151a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/subclassing-ignored.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.ZonedDateTime,
+ [10n, "UTC"],
+ "with",
+ [{ year: 2000 }],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 946684800_000_000_010n, "epochNanoseconds result");
+ assert.sameValue(result.year, 2000, "year result");
+ assert.sameValue(result.month, 1, "month result");
+ assert.sameValue(result.day, 1, "day result");
+ assert.sameValue(result.hour, 0, "hour result");
+ assert.sameValue(result.minute, 0, "minute result");
+ assert.sameValue(result.second, 0, "second result");
+ assert.sameValue(result.millisecond, 0, "millisecond result");
+ assert.sameValue(result.microsecond, 0, "microsecond result");
+ assert.sameValue(result.nanosecond, 10, "nanosecond result");
+ },
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..404a64aed90
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.with({ day: 27 }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..2689ffea0f8
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.with({ day: 27 }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..3dfdaf445bb
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.with({ day: 27 }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getpossibleinstantsfor-iterable.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getpossibleinstantsfor-iterable.js
new file mode 100644
index 00000000000..9c3e6900bc7
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getpossibleinstantsfor-iterable.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.with
+description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
+info: |
+ sec-temporal.zoneddatetime.prototype.with step 24:
+ 24. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_dateTimeResult_.[[Year]], [...], _dateTimeResult_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_).
+ sec-temporal-interpretisodatetimeoffset step 7:
+ 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
+ sec-temporal-getpossibleinstantsfor step 2:
+ 2. Let _list_ be ? IterableToList(_possibleInstants_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "2005-09-09T01:46:40",
+];
+
+TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone);
+ datetime.with({ year: 2005 });
+}, expected);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/builtin.js
new file mode 100644
index 00000000000..63a75d030f5
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withcalendar
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.withCalendar
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.withCalendar),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.withCalendar),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.withCalendar),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.withCalendar.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-temporal-object.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-temporal-object.js
new file mode 100644
index 00000000000..c62b8ade3d5
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-temporal-object.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withcalendar
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.zoneddatetime.prototype.withcalendar step 3:
+ 3. Let _calendar_ be ? ToTemporalCalendar(_calendarLike_).
+ 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 zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const result = zonedDateTime.withCalendar(temporalObject);
+ assert.sameValue(result.calendar, calendar, "Temporal object coerced to calendar");
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/length.js
new file mode 100644
index 00000000000..296328461a4
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withcalendar
+description: Temporal.ZonedDateTime.prototype.withCalendar.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.withCalendar, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/missing-argument.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/missing-argument.js
new file mode 100644
index 00000000000..4ae04818fba
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/missing-argument.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withcalendar
+description: RangeError thrown when calendar argument not given
+features: [Temporal]
+---*/
+
+const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+assert.throws(RangeError, () => zonedDateTime.withCalendar(), "missing argument");
+assert.throws(RangeError, () => zonedDateTime.withCalendar(undefined), "undefined argument");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/name.js
new file mode 100644
index 00000000000..b8b16f082cd
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withcalendar
+description: Temporal.ZonedDateTime.prototype.withCalendar.name is "withCalendar".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.withCalendar, "name", {
+ value: "withCalendar",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/not-a-constructor.js
new file mode 100644
index 00000000000..15a968e90e2
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withcalendar
+description: >
+ Temporal.ZonedDateTime.prototype.withCalendar does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.withCalendar();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.withCalendar), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.withCalendar)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/prop-desc.js
new file mode 100644
index 00000000000..65fc4c72a01
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withcalendar
+description: The "withCalendar" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.withCalendar,
+ "function",
+ "`typeof ZonedDateTime.prototype.withCalendar` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "withCalendar", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/subclassing-ignored.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/subclassing-ignored.js
new file mode 100644
index 00000000000..f044a122de3
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/subclassing-ignored.js
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withcalendar
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const customCalendar = {
+ year() { return 1900; },
+ month() { return 2; },
+ day() { return 5; },
+ toString() { return "custom-calendar"; },
+};
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.ZonedDateTime,
+ [10n, "UTC"],
+ "withCalendar",
+ [customCalendar],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 10n, "epochNanoseconds result");
+ assert.sameValue(result.year, 1900, "year result");
+ assert.sameValue(result.month, 2, "month result");
+ assert.sameValue(result.day, 5, "day result");
+ assert.sameValue(result.hour, 0, "hour result");
+ assert.sameValue(result.minute, 0, "minute result");
+ assert.sameValue(result.second, 0, "second result");
+ assert.sameValue(result.millisecond, 0, "millisecond result");
+ assert.sameValue(result.microsecond, 0, "microsecond result");
+ assert.sameValue(result.nanosecond, 10, "nanosecond result");
+ assert.sameValue(result.calendar, customCalendar, "calendar result");
+ },
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-plaindatetime.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-plaindatetime.js
new file mode 100644
index 00000000000..c9da210d06a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-plaindatetime.js
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.withplaindate
+description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots
+info: |
+ sec-temporal.zoneddatetime.prototype.withplaindate step 3:
+ 3. Let _plainDate_ be ? ToTemporalDate(_plainDateLike_).
+ sec-temporal-totemporaldate step 2.b:
+ b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then
+ i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => {
+ const receiver = new Temporal.ZonedDateTime(1_000_000_000_123_456_789n, "UTC");
+ const result = receiver.withPlainDate(datetime);
+ assert.sameValue(result.year, 2000, "year result");
+ assert.sameValue(result.month, 5, "month result");
+ assert.sameValue(result.day, 2, "day result");
+ assert.sameValue(result.hour, 1, "hour result");
+ assert.sameValue(result.minute, 46, "minute result");
+ assert.sameValue(result.second, 40, "second result");
+ assert.sameValue(result.millisecond, 123, "millisecond result");
+ assert.sameValue(result.microsecond, 456, "microsecond result");
+ assert.sameValue(result.nanosecond, 789, "nanosecond result");
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..d3d04181c97
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaindate
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const other = new Temporal.ZonedDateTime(1_100_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.withPlainDate(other));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..7ccdef857c8
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaindate
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const other = new Temporal.ZonedDateTime(1_100_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.withPlainDate(other));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..61187f599f9
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaindate
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const other = new Temporal.ZonedDateTime(1_100_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.withPlainDate(other));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin.js
new file mode 100644
index 00000000000..2b93ea5fc42
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaindate
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.withPlainDate
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.withPlainDate),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.withPlainDate),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.withPlainDate),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.withPlainDate.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-dateadd-called-with-options-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-dateadd-called-with-options-undefined.js
new file mode 100644
index 00000000000..8b22fa81dcd
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-dateadd-called-with-options-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaindate
+description: >
+ BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the
+ options value
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddUndefinedOptions();
+const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9);
+const instance = new Temporal.ZonedDateTime(82800_000_000_000n, timeZone, calendar);
+instance.withPlainDate(new Temporal.PlainDate(1970, 1, 1, calendar));
+assert.sameValue(calendar.dateAddCallCount, 1);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-fields-iterable.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-fields-iterable.js
new file mode 100644
index 00000000000..b0e3906c3a7
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-fields-iterable.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaindate
+description: Verify the result of calendar.fields() is treated correctly.
+info: |
+ sec-temporal.zoneddatetime.prototype.withplaindate step 3:
+ 3. Let _plainDate_ be ? ToTemporalDate(_plainDateLike_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
+ sec-temporal-calendarfields step 4:
+ 4. Let _result_ be ? IterableToList(_fieldsArray_).
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "day",
+ "month",
+ "monthCode",
+ "year",
+];
+
+const calendar1 = TemporalHelpers.calendarFieldsIterable();
+const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar1);
+const calendar2 = TemporalHelpers.calendarFieldsIterable();
+datetime.withPlainDate({ year: 2001, month: 6, day: 4, calendar: calendar2 });
+
+assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called");
+assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once");
+assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args");
+assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-temporal-object.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-temporal-object.js
new file mode 100644
index 00000000000..a7423b74a8b
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-temporal-object.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaindate
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.zoneddatetime.prototype.withplaindate step 3:
+ 3. Let _plainDate_ be ? ToTemporalDate(_plainDateLike_).
+ sec-temporal-totemporaldate step 2.c:
+ c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
+ sec-temporal-gettemporalcalendarwithisodefault step 2:
+ 2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
+ sec-temporal-totemporalcalendarwithisodefault step 2:
+ 3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => {
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+ // the PlainDate's calendar will override the ZonedDateTime's ISO calendar
+ const result = datetime.withPlainDate({ year: 2001, month: 6, day: 4, calendar: temporalObject });
+ assert.sameValue(result.calendar, calendar, "Temporal object coerced to calendar");
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..cbed462de7b
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.zoneddatetime.prototype.withplaindate
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
+const base = { year: 2000, month: 5, day: 2 };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["year", "month", "day"].forEach((prop) => {
+ assert.throws(RangeError, () => instance.withPlainDate({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
+ assert.throws(RangeError, () => instance.withPlainDate({ ...base, [prop]: obj }));
+ assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/length.js
new file mode 100644
index 00000000000..f307fcb1a65
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaindate
+description: Temporal.ZonedDateTime.prototype.withPlainDate.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.withPlainDate, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/name.js
new file mode 100644
index 00000000000..1b36a0deadd
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaindate
+description: Temporal.ZonedDateTime.prototype.withPlainDate.name is "withPlainDate".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.withPlainDate, "name", {
+ value: "withPlainDate",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/negative-epochnanoseconds.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/negative-epochnanoseconds.js
new file mode 100644
index 00000000000..46aee7faabe
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/negative-epochnanoseconds.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaindate
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const result = datetime.withPlainDate(new Temporal.PlainDate(2000, 5, 2));
+assert.sameValue(result.epochNanoseconds, 957286235_000_000_001n);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/not-a-constructor.js
new file mode 100644
index 00000000000..b68389c619c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaindate
+description: >
+ Temporal.ZonedDateTime.prototype.withPlainDate does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.withPlainDate();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.withPlainDate), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.withPlainDate)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/prop-desc.js
new file mode 100644
index 00000000000..9c703be6d4c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaindate
+description: The "withPlainDate" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.withPlainDate,
+ "function",
+ "`typeof ZonedDateTime.prototype.withPlainDate` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "withPlainDate", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/subclassing-ignored.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/subclassing-ignored.js
new file mode 100644
index 00000000000..cb6101cc64a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/subclassing-ignored.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaindate
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.ZonedDateTime,
+ [10n, "UTC"],
+ "withPlainDate",
+ ["2000-01-01"],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 946684800_000_000_010n, "epochNanoseconds result");
+ assert.sameValue(result.year, 2000, "year result");
+ assert.sameValue(result.month, 1, "month result");
+ assert.sameValue(result.day, 1, "day result");
+ assert.sameValue(result.hour, 0, "hour result");
+ assert.sameValue(result.minute, 0, "minute result");
+ assert.sameValue(result.second, 0, "second result");
+ assert.sameValue(result.millisecond, 0, "millisecond result");
+ assert.sameValue(result.microsecond, 0, "microsecond result");
+ assert.sameValue(result.nanosecond, 10, "nanosecond result");
+ },
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..ce1342ae469
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaindate
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ assert.throws(RangeError, () => datetime.withPlainDate(date));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..9a6f4de3dae
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaindate
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ assert.throws(RangeError, () => datetime.withPlainDate(date));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..5aad76dbf78
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaindate
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const date = new Temporal.PlainDate(2000, 5, 2);
+ assert.throws(TypeError, () => datetime.withPlainDate(date));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-balance-negative-time-units.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 00000000000..ab2372b537e
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,45 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaintime
+description: Negative time fields are balanced upwards if the argument is given as ZonedDateTime
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-totemporaltime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Set _plainDateTime_ to ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.zoneddatetime.prototype.withplaintime step 4.a:
+ a. Let _plainTime_ be ? ToTemporalTime(_plainTimeLike_).
+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 = new Temporal.TimeZone("-00:00:00.000000002");
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+const otherTimeZone = new Temporal.TimeZone("UTC"); // should not be used to convert datetime -> PlainTime
+const zdt = new Temporal.ZonedDateTime(86400_000_000_000n, otherTimeZone);
+const newzdt = zdt.withPlainTime(datetime);
+
+assert.sameValue(newzdt.microsecond, 0);
+assert.sameValue(newzdt.nanosecond, 999);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-negative-epochnanoseconds.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 00000000000..4a8952bc750
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaintime
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 106.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+const result = instance.withPlainTime(datetime);
+assert.sameValue(result.epochNanoseconds, 60635_000_000_001n);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..4606a4f4f01
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaintime
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const other = new Temporal.ZonedDateTime(1_100_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.withPlainTime(other));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..98ad066bb44
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaintime
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const other = new Temporal.ZonedDateTime(1_100_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.withPlainTime(other));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..db12e43dec0
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaintime
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ const other = new Temporal.ZonedDateTime(1_100_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.withPlainTime(other));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/builtin.js
new file mode 100644
index 00000000000..61576da0fa7
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaintime
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.withPlainTime
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.withPlainTime),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.withPlainTime),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.withPlainTime),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.withPlainTime.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/calendar-dateadd-called-with-options-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/calendar-dateadd-called-with-options-undefined.js
new file mode 100644
index 00000000000..387aeacb844
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/calendar-dateadd-called-with-options-undefined.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaintime
+description: >
+ BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the
+ options value
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const calendar = TemporalHelpers.calendarDateAddUndefinedOptions();
+const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9);
+const instance = new Temporal.ZonedDateTime(7200_000_000_000n, timeZone, calendar);
+instance.withPlainTime();
+assert.sameValue(calendar.dateAddCallCount, 1);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/calendar-temporal-object.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/calendar-temporal-object.js
new file mode 100644
index 00000000000..dc5866225db
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/calendar-temporal-object.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.withplaintime
+description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
+info: |
+ sec-temporal.plaindatetime.prototype.withplaintime step 4:
+ 3. Let _plainTime_ be ? ToTemporalTime(_plainTimeLike_).
+ sec-temporal-totemporaltime step 3.d:
+ d. If _calendar_ is not *undefined*, then
+ i. Set _calendar_ to ? ToTemporalCalendar(_calendar_).
+ ii. If ? ToString(_calendar_) is not *"iso8601"*, then
+ 1. Throw a *RangeError* exception.
+ sec-temporal-totemporalcalendar step 1.a:
+ a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+ i. Return _temporalCalendarLike_.[[Calendar]].
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
+ const datetime = new Temporal.PlainDateTime(2000, 5, 3, 13, 3, 27, 123, 456, 789);
+ assert.throws(RangeError, () => datetime.withPlainTime({ hour: 12, minute: 30, calendar: temporalObject }));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/length.js
new file mode 100644
index 00000000000..09cc7b6fdc8
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaintime
+description: Temporal.ZonedDateTime.prototype.withPlainTime.length is 0
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.withPlainTime, "length", {
+ value: 0,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/name.js
new file mode 100644
index 00000000000..8a04cebfd5c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaintime
+description: Temporal.ZonedDateTime.prototype.withPlainTime.name is "withPlainTime".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.withPlainTime, "name", {
+ value: "withPlainTime",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/not-a-constructor.js
new file mode 100644
index 00000000000..4a654284573
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaintime
+description: >
+ Temporal.ZonedDateTime.prototype.withPlainTime does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.withPlainTime();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.withPlainTime), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.withPlainTime)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/prop-desc.js
new file mode 100644
index 00000000000..b81669d5746
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaintime
+description: The "withPlainTime" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.withPlainTime,
+ "function",
+ "`typeof ZonedDateTime.prototype.withPlainTime` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "withPlainTime", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/subclassing-ignored.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/subclassing-ignored.js
new file mode 100644
index 00000000000..c8526caa829
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/subclassing-ignored.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaintime
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.ZonedDateTime,
+ [10n, "UTC"],
+ "withPlainTime",
+ ["05:43:21.123456789"],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 20601_123_456_789n, "epochNanoseconds result");
+ assert.sameValue(result.year, 1970, "year result");
+ assert.sameValue(result.month, 1, "month result");
+ assert.sameValue(result.day, 1, "day result");
+ assert.sameValue(result.hour, 5, "hour result");
+ assert.sameValue(result.minute, 43, "minute result");
+ assert.sameValue(result.second, 21, "second result");
+ assert.sameValue(result.millisecond, 123, "millisecond result");
+ assert.sameValue(result.microsecond, 456, "microsecond result");
+ assert.sameValue(result.nanosecond, 789, "nanosecond result");
+ },
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/time-undefined.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/time-undefined.js
new file mode 100644
index 00000000000..a8482e38043
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/time-undefined.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaintime
+description: The time is assumed to be midnight if not given
+features: [BigInt, Temporal]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(957270896_987_654_321n, "UTC");
+
+const explicit = datetime.withPlainTime(undefined);
+assert.sameValue(explicit.hour, 0, "default time is midnight");
+assert.sameValue(explicit.minute, 0, "default time is midnight");
+assert.sameValue(explicit.second, 0, "default time is midnight");
+assert.sameValue(explicit.millisecond, 0, "default time is midnight");
+assert.sameValue(explicit.microsecond, 0, "default time is midnight");
+assert.sameValue(explicit.nanosecond, 0, "default time is midnight");
+
+const implicit = datetime.withPlainTime();
+assert.sameValue(implicit.hour, 0, "default time is midnight");
+assert.sameValue(implicit.minute, 0, "default time is midnight");
+assert.sameValue(implicit.second, 0, "default time is midnight");
+assert.sameValue(implicit.millisecond, 0, "default time is midnight");
+assert.sameValue(implicit.microsecond, 0, "default time is midnight");
+assert.sameValue(implicit.nanosecond, 0, "default time is midnight");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..9c6016214d2
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaintime
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ assert.throws(RangeError, () => datetime.withPlainTime(time));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..09d5b9258c2
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaintime
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_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, () => datetime.withPlainTime(time));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..175ca62a1ca
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withplaintime
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ assert.throws(TypeError, () => datetime.withPlainTime(time));
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/builtin.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/builtin.js
new file mode 100644
index 00000000000..5875c7b21b2
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withtimezone
+description: >
+ Tests that Temporal.ZonedDateTime.prototype.withTimeZone
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.ZonedDateTime.prototype.withTimeZone),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.withTimeZone),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.withTimeZone),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.ZonedDateTime.prototype.withTimeZone.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/length.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/length.js
new file mode 100644
index 00000000000..d6d71b00ceb
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withtimezone
+description: Temporal.ZonedDateTime.prototype.withTimeZone.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.withTimeZone, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/name.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/name.js
new file mode 100644
index 00000000000..dd27d1edb4e
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withtimezone
+description: Temporal.ZonedDateTime.prototype.withTimeZone.name is "withTimeZone".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.ZonedDateTime.prototype.withTimeZone, "name", {
+ value: "withTimeZone",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/not-a-constructor.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/not-a-constructor.js
new file mode 100644
index 00000000000..5493995f349
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withtimezone
+description: >
+ Temporal.ZonedDateTime.prototype.withTimeZone does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.ZonedDateTime.prototype.withTimeZone();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.withTimeZone), false,
+ "isConstructor(Temporal.ZonedDateTime.prototype.withTimeZone)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/prop-desc.js
new file mode 100644
index 00000000000..6e377ecd329
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withtimezone
+description: The "withTimeZone" property of Temporal.ZonedDateTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.ZonedDateTime.prototype.withTimeZone,
+ "function",
+ "`typeof ZonedDateTime.prototype.withTimeZone` is `function`"
+);
+
+verifyProperty(Temporal.ZonedDateTime.prototype, "withTimeZone", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/subclassing-ignored.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/subclassing-ignored.js
new file mode 100644
index 00000000000..c602257844c
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/subclassing-ignored.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withtimezone
+description: Objects of a subclass are never created as return values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.checkSubclassingIgnored(
+ Temporal.ZonedDateTime,
+ [10n, "UTC"],
+ "withTimeZone",
+ ["+01:00"],
+ (result) => {
+ assert.sameValue(result.epochNanoseconds, 10n, "epochNanoseconds result");
+ assert.sameValue(result.year, 1970, "year result");
+ assert.sameValue(result.month, 1, "month result");
+ assert.sameValue(result.day, 1, "day result");
+ assert.sameValue(result.hour, 1, "hour result");
+ assert.sameValue(result.minute, 0, "minute result");
+ assert.sameValue(result.second, 0, "second result");
+ assert.sameValue(result.millisecond, 0, "millisecond result");
+ assert.sameValue(result.microsecond, 0, "microsecond result");
+ assert.sameValue(result.nanosecond, 10, "nanosecond result");
+ },
+);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-datetime.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-datetime.js
new file mode 100644
index 00000000000..1130b6d5e7a
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-datetime.js
@@ -0,0 +1,44 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime.prototype.withtimezone
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(0n, "UTC");
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => instance.withTimeZone(timeZone), "bare date-time string is not a time zone");
+assert.throws(RangeError, () => instance.withTimeZone({ timeZone }), "bare date-time string is not a time zone");
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = instance.withTimeZone(timeZone);
+assert.sameValue(result1.timeZone.id, "UTC", "date-time + Z is UTC time zone");
+const result2 = instance.withTimeZone({ timeZone });
+assert.sameValue(result2.timeZone.id, "UTC", "date-time + Z is UTC time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result3 = instance.withTimeZone(timeZone);
+assert.sameValue(result3.timeZone.id, "-07:00", "date-time + offset is the offset time zone");
+const result4 = instance.withTimeZone({ timeZone });
+assert.sameValue(result4.timeZone.id, "-07:00", "date-time + offset is the offset time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30[America/Vancouver]";
+const result5 = instance.withTimeZone(timeZone);
+assert.sameValue(result5.timeZone.id, "America/Vancouver", "date-time + IANA annotation is the IANA time zone");
+const result6 = instance.withTimeZone({ timeZone });
+assert.sameValue(result6.timeZone.id, "America/Vancouver", "date-time + IANA annotation is the IANA time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30Z[America/Vancouver]";
+const result7 = instance.withTimeZone(timeZone);
+assert.sameValue(result7.timeZone.id, "America/Vancouver", "date-time + Z + IANA annotation is the IANA time zone");
+const result8 = instance.withTimeZone({ timeZone });
+assert.sameValue(result8.timeZone.id, "America/Vancouver", "date-time + Z + IANA annotation is the IANA time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30-07:00[America/Vancouver]";
+const result9 = instance.withTimeZone(timeZone);
+assert.sameValue(result9.timeZone.id, "America/Vancouver", "date-time + offset + IANA annotation is the IANA time zone");
+const result10 = instance.withTimeZone({ timeZone });
+assert.sameValue(result10.timeZone.id, "America/Vancouver", "date-time + offset + IANA annotation is the IANA time zone (string in property bag)");
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/year/calendar-returns-infinity.js b/test/built-ins/Temporal/ZonedDateTime/prototype/year/calendar-returns-infinity.js
new file mode 100644
index 00000000000..2eb34813bc6
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/year/calendar-returns-infinity.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.year
+description: Getter throws if the calendar returns ±∞ from its year method
+features: [Temporal]
+---*/
+
+class InfinityCalendar extends Temporal.Calendar {
+ constructor(positive) {
+ super("iso8601");
+ this.positive = positive;
+ }
+
+ year() {
+ return this.positive ? Infinity : -Infinity;
+ }
+}
+
+const pos = new InfinityCalendar(true);
+const instance1 = new Temporal.ZonedDateTime(0n, "UTC", pos);
+assert.throws(RangeError, () => instance1.year);
+
+const neg = new InfinityCalendar(false);
+const instance2 = new Temporal.ZonedDateTime(0n, "UTC", neg);
+assert.throws(RangeError, () => instance2.year);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/year/prop-desc.js b/test/built-ins/Temporal/ZonedDateTime/prototype/year/prop-desc.js
new file mode 100644
index 00000000000..0b5e639b81e
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/year/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.year
+description: The "year" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "year");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-non-integer.js b/test/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..ee36fb294ba
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.year
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.year);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..d03fa5e9f28
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.year
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.year);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..ede6e0049fa
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.year
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.year);
+});
diff --git a/test/built-ins/Temporal/ZonedDateTime/timezone-string-datetime.js b/test/built-ins/Temporal/ZonedDateTime/timezone-string-datetime.js
new file mode 100644
index 00000000000..27cb7502b84
--- /dev/null
+++ b/test/built-ins/Temporal/ZonedDateTime/timezone-string-datetime.js
@@ -0,0 +1,42 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.zoneddatetime
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+let timeZone = "2021-08-19T17:30";
+assert.throws(RangeError, () => new Temporal.ZonedDateTime(0n, timeZone), "bare date-time string is not a time zone");
+assert.throws(RangeError, () => new Temporal.ZonedDateTime(0n, { timeZone }), "bare date-time string is not a time zone");
+
+timeZone = "2021-08-19T17:30Z";
+const result1 = new Temporal.ZonedDateTime(0n, timeZone);
+assert.sameValue(result1.timeZone.id, "UTC", "date-time + Z is UTC time zone");
+const result2 = new Temporal.ZonedDateTime(0n, { timeZone });
+assert.sameValue(result2.timeZone.id, "UTC", "date-time + Z is UTC time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30-07:00";
+const result3 = new Temporal.ZonedDateTime(0n, timeZone);
+assert.sameValue(result3.timeZone.id, "-07:00", "date-time + offset is the offset time zone");
+const result4 = new Temporal.ZonedDateTime(0n, { timeZone });
+assert.sameValue(result4.timeZone.id, "-07:00", "date-time + offset is the offset time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30[America/Vancouver]";
+const result5 = new Temporal.ZonedDateTime(0n, timeZone);
+assert.sameValue(result5.timeZone.id, "America/Vancouver", "date-time + IANA annotation is the IANA time zone");
+const result6 = new Temporal.ZonedDateTime(0n, { timeZone });
+assert.sameValue(result6.timeZone.id, "America/Vancouver", "date-time + IANA annotation is the IANA time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30Z[America/Vancouver]";
+const result7 = new Temporal.ZonedDateTime(0n, timeZone);
+assert.sameValue(result7.timeZone.id, "America/Vancouver", "date-time + Z + IANA annotation is the IANA time zone");
+const result8 = new Temporal.ZonedDateTime(0n, { timeZone });
+assert.sameValue(result8.timeZone.id, "America/Vancouver", "date-time + Z + IANA annotation is the IANA time zone (string in property bag)");
+
+timeZone = "2021-08-19T17:30-07:00[America/Vancouver]";
+const result9 = new Temporal.ZonedDateTime(0n, timeZone);
+assert.sameValue(result9.timeZone.id, "America/Vancouver", "date-time + offset + IANA annotation is the IANA time zone");
+const result10 = new Temporal.ZonedDateTime(0n, { timeZone });
+assert.sameValue(result10.timeZone.id, "America/Vancouver", "date-time + offset + IANA annotation is the IANA time zone (string in property bag)");
diff --git a/test/built-ins/Temporal/getOwnPropertyNames.js b/test/built-ins/Temporal/getOwnPropertyNames.js
new file mode 100644
index 00000000000..02de8fa0e94
--- /dev/null
+++ b/test/built-ins/Temporal/getOwnPropertyNames.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-objects
+description: Temporal has own property names
+includes: [arrayContains.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ "Instant",
+ "TimeZone",
+ "PlainDate",
+ "PlainTime",
+ "PlainDateTime",
+ "ZonedDateTime",
+ "PlainYearMonth",
+ "PlainMonthDay",
+ "Duration",
+ "Calendar",
+ "Now",
+];
+const keys = Object.getOwnPropertyNames(Temporal);
+assert.sameValue(keys.length, expected.length, "length");
+assert(arrayContains(keys, expected), "contents");
diff --git a/test/built-ins/Temporal/keys.js b/test/built-ins/Temporal/keys.js
new file mode 100644
index 00000000000..16e33be3b37
--- /dev/null
+++ b/test/built-ins/Temporal/keys.js
@@ -0,0 +1,12 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-objects
+description: Temporal has no enumerable properties
+includes: [compareArray.js]
+features: [Temporal]
+---*/
+
+const keys = Object.keys(Temporal);
+assert.compareArray(keys, []);
diff --git a/test/built-ins/Temporal/prop-desc.js b/test/built-ins/Temporal/prop-desc.js
new file mode 100644
index 00000000000..6018ceabe92
--- /dev/null
+++ b/test/built-ins/Temporal/prop-desc.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-objects
+includes: [propertyHelper.js]
+description: The "Temporal" property of the global object
+features: [Temporal]
+---*/
+
+assert.sameValue(typeof Temporal, "object");
+verifyProperty(this, "Temporal", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/toStringTag/prop-desc.js b/test/built-ins/Temporal/toStringTag/prop-desc.js
new file mode 100644
index 00000000000..bbf0b174bde
--- /dev/null
+++ b/test/built-ins/Temporal/toStringTag/prop-desc.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-@@tostringtag
+description: The @@toStringTag property of Temporal
+includes: [propertyHelper.js]
+features: [Symbol.toStringTag, Temporal]
+---*/
+
+verifyProperty(Temporal, Symbol.toStringTag, {
+ value: "Temporal",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/built-ins/Temporal/toStringTag/string.js b/test/built-ins/Temporal/toStringTag/string.js
new file mode 100644
index 00000000000..eeaa7f94efb
--- /dev/null
+++ b/test/built-ins/Temporal/toStringTag/string.js
@@ -0,0 +1,10 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-@@tostringtag
+description: The @@toStringTag property of Temporal produces the correct value in toString
+features: [Symbol.toStringTag, Temporal]
+---*/
+
+assert.sameValue(String(Temporal), "[object Temporal]");
diff --git a/test/intl402/DateTimeFormat/prototype/format/temporal-objects-resolved-time-zone.js b/test/intl402/DateTimeFormat/prototype/format/temporal-objects-resolved-time-zone.js
new file mode 100644
index 00000000000..7abef1bb3d8
--- /dev/null
+++ b/test/intl402/DateTimeFormat/prototype/format/temporal-objects-resolved-time-zone.js
@@ -0,0 +1,37 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-datetime-format-functions
+description: A time zone in resolvedOptions with a large offset still produces the correct string
+locale: [en]
+features: [Temporal]
+---*/
+
+const formatter = new Intl.DateTimeFormat("en", { timeZone: "Pacific/Apia" });
+
+const date = new Temporal.PlainDate(2021, 8, 4);
+const dateResult = formatter.format(date);
+assert.sameValue(dateResult, "8/4/2021", "plain date");
+
+const datetime1 = new Temporal.PlainDateTime(2021, 8, 4, 0, 30, 45, 123, 456, 789);
+const datetimeResult1 = formatter.format(datetime1);
+assert.sameValue(datetimeResult1, "8/4/2021, 12:30:45 AM", "plain datetime close to beginning of day");
+const datetime2 = new Temporal.PlainDateTime(2021, 8, 4, 23, 30, 45, 123, 456, 789);
+const datetimeResult2 = formatter.format(datetime2);
+assert.sameValue(datetimeResult2, "8/4/2021, 11:30:45 PM", "plain datetime close to end of day");
+
+const monthDay = new Temporal.PlainMonthDay(8, 4, "gregory");
+const monthDayResult = formatter.format(monthDay);
+assert.sameValue(monthDayResult, "8/4", "plain month-day");
+
+const time1 = new Temporal.PlainTime(0, 30, 45, 123, 456, 789);
+const timeResult1 = formatter.format(time1);
+assert.sameValue(timeResult1, "12:30:45 AM", "plain time close to beginning of day");
+const time2 = new Temporal.PlainTime(23, 30, 45, 123, 456, 789);
+const timeResult2 = formatter.format(time2);
+assert.sameValue(timeResult2, "11:30:45 PM", "plain time close to end of day");
+
+const month = new Temporal.PlainYearMonth(2021, 8, "gregory");
+const monthResult = formatter.format(month);
+assert.sameValue(monthResult, "8/2021", "plain year-month");
diff --git a/test/intl402/DateTimeFormat/prototype/formatRange/temporal-objects-resolved-time-zone.js b/test/intl402/DateTimeFormat/prototype/formatRange/temporal-objects-resolved-time-zone.js
new file mode 100644
index 00000000000..3e495ceedb3
--- /dev/null
+++ b/test/intl402/DateTimeFormat/prototype/formatRange/temporal-objects-resolved-time-zone.js
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-datetime-format-functions
+description: A time zone in resolvedOptions with a large offset still produces the correct string
+locale: [en]
+features: [Temporal, Intl.DateTimeFormat-formatRange]
+---*/
+
+const formatter = new Intl.DateTimeFormat("en", { timeZone: "Pacific/Apia" });
+
+const date1 = new Temporal.PlainDate(2021, 8, 4);
+const date2 = new Temporal.PlainDate(2021, 8, 5);
+const dateResult = formatter.formatRange(date1, date2);
+assert.sameValue(dateResult, "8/4/2021 – 8/5/2021", "plain dates");
+
+const datetime1 = new Temporal.PlainDateTime(2021, 8, 4, 0, 30, 45, 123, 456, 789);
+const datetime2 = new Temporal.PlainDateTime(2021, 8, 4, 23, 30, 45, 123, 456, 789);
+const datetimeResult = formatter.formatRange(datetime1, datetime2);
+assert.sameValue(datetimeResult, "8/4/2021, 12:30:45 AM – 11:30:45 PM", "plain datetimes");
+
+const monthDay1 = new Temporal.PlainMonthDay(8, 4, "gregory");
+const monthDay2 = new Temporal.PlainMonthDay(8, 5, "gregory");
+const monthDayResult = formatter.formatRange(monthDay1, monthDay2);
+assert.sameValue(monthDayResult, "8/4 – 8/5", "plain month-days");
+
+const time1 = new Temporal.PlainTime(0, 30, 45, 123, 456, 789);
+const time2 = new Temporal.PlainTime(23, 30, 45, 123, 456, 789);
+const timeResult = formatter.formatRange(time1, time2);
+assert.sameValue(timeResult, "12:30:45 AM – 11:30:45 PM", "plain times");
+
+const month1 = new Temporal.PlainYearMonth(2021, 8, "gregory");
+const month2 = new Temporal.PlainYearMonth(2021, 9, "gregory");
+const monthResult = formatter.formatRange(month1, month2);
+assert.sameValue(monthResult, "8/2021 – 9/2021", "plain year-months");
diff --git a/test/intl402/DateTimeFormat/prototype/formatRangeToParts/temporal-objects-resolved-time-zone.js b/test/intl402/DateTimeFormat/prototype/formatRangeToParts/temporal-objects-resolved-time-zone.js
new file mode 100644
index 00000000000..1c433c431fc
--- /dev/null
+++ b/test/intl402/DateTimeFormat/prototype/formatRangeToParts/temporal-objects-resolved-time-zone.js
@@ -0,0 +1,103 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-datetime-format-functions
+description: A time zone in resolvedOptions with a large offset still produces the correct string
+locale: [en]
+includes: [deepEqual.js]
+features: [Temporal, Intl.DateTimeFormat-formatRange]
+---*/
+
+const formatter = new Intl.DateTimeFormat("en", { timeZone: "Pacific/Apia" });
+
+const date1 = new Temporal.PlainDate(2021, 8, 4);
+const date2 = new Temporal.PlainDate(2021, 8, 5);
+const dateResult = formatter.formatRangeToParts(date1, date2);
+assert.deepEqual(dateResult, [
+ { type: "month", value: "8", source: "startRange" },
+ { type: "literal", value: "/", source: "startRange" },
+ { type: "day", value: "4", source: "startRange" },
+ { type: "literal", value: "/", source: "startRange" },
+ { type: "year", value: "2021", source: "startRange" },
+ { type: "literal", value: " – ", source: "shared" },
+ { type: "month", value: "8", source: "endRange" },
+ { type: "literal", value: "/", source: "endRange" },
+ { type: "day", value: "5", source: "endRange" },
+ { type: "literal", value: "/", source: "endRange" },
+ { type: "year", value: "2021", source: "endRange" },
+], "plain dates");
+
+const datetime1 = new Temporal.PlainDateTime(2021, 8, 4, 0, 30, 45, 123, 456, 789);
+const datetime2 = new Temporal.PlainDateTime(2021, 8, 4, 23, 30, 45, 123, 456, 789);
+const datetimeResult = formatter.formatRangeToParts(datetime1, datetime2);
+assert.deepEqual(datetimeResult, [
+ { type: "month", value: "8", source: "shared" },
+ { type: "literal", value: "/", source: "shared" },
+ { type: "day", value: "4", source: "shared" },
+ { type: "literal", value: "/", source: "shared" },
+ { type: "year", value: "2021", source: "shared" },
+ { type: "literal", value: ", ", source: "shared" },
+ { type: "hour", value: "12", source: "startRange" },
+ { type: "literal", value: ":", source: "startRange" },
+ { type: "minute", value: "30", source: "startRange" },
+ { type: "literal", value: ":", source: "startRange" },
+ { type: "second", value: "45", source: "startRange" },
+ { type: "literal", value: " ", source: "startRange" },
+ { type: "dayPeriod", value: "AM", source: "startRange" },
+ { type: "literal", value: " – ", source: "shared" },
+ { type: "hour", value: "11", source: "endRange" },
+ { type: "literal", value: ":", source: "endRange" },
+ { type: "minute", value: "30", source: "endRange" },
+ { type: "literal", value: ":", source: "endRange" },
+ { type: "second", value: "45", source: "endRange" },
+ { type: "literal", value: " ", source: "endRange" },
+ { type: "dayPeriod", value: "PM", source: "endRange" },
+], "plain datetimes");
+
+const monthDay1 = new Temporal.PlainMonthDay(8, 4, "gregory");
+const monthDay2 = new Temporal.PlainMonthDay(8, 5, "gregory");
+const monthDayResult = formatter.formatRangeToParts(monthDay1, monthDay2);
+assert.deepEqual(monthDayResult, [
+ { type: "month", value: "8", source: "startRange" },
+ { type: "literal", value: "/", source: "startRange" },
+ { type: "day", value: "4", source: "startRange" },
+ { type: "literal", value: " – ", source: "shared" },
+ { type: "month", value: "8", source: "endRange" },
+ { type: "literal", value: "/", source: "endRange" },
+ { type: "day", value: "5", source: "endRange" },
+], "plain month-days");
+
+const time1 = new Temporal.PlainTime(0, 30, 45, 123, 456, 789);
+const time2 = new Temporal.PlainTime(23, 30, 45, 123, 456, 789);
+const timeResult = formatter.formatRangeToParts(time1, time2);
+assert.deepEqual(timeResult, [
+ { type: "hour", value: "12", source: "startRange" },
+ { type: "literal", value: ":", source: "startRange" },
+ { type: "minute", value: "30", source: "startRange" },
+ { type: "literal", value: ":", source: "startRange" },
+ { type: "second", value: "45", source: "startRange" },
+ { type: "literal", value: " ", source: "startRange" },
+ { type: "dayPeriod", value: "AM", source: "startRange" },
+ { type: "literal", value: " – ", source: "shared" },
+ { type: "hour", value: "11", source: "endRange" },
+ { type: "literal", value: ":", source: "endRange" },
+ { type: "minute", value: "30", source: "endRange" },
+ { type: "literal", value: ":", source: "endRange" },
+ { type: "second", value: "45", source: "endRange" },
+ { type: "literal", value: " ", source: "endRange" },
+ { type: "dayPeriod", value: "PM", source: "endRange" },
+], "plain times");
+
+const month1 = new Temporal.PlainYearMonth(2021, 8, "gregory");
+const month2 = new Temporal.PlainYearMonth(2021, 9, "gregory");
+const monthResult = formatter.formatRangeToParts(month1, month2);
+assert.deepEqual(monthResult, [
+ { type: "month", value: "8", source: "startRange" },
+ { type: "literal", value: "/", source: "startRange" },
+ { type: "year", value: "2021", source: "startRange" },
+ { type: "literal", value: " – ", source: "shared" },
+ { type: "month", value: "9", source: "endRange" },
+ { type: "literal", value: "/", source: "endRange" },
+ { type: "year", value: "2021", source: "endRange" },
+], "plain year-months");
diff --git a/test/intl402/DateTimeFormat/prototype/formatToParts/temporal-objects-resolved-time-zone.js b/test/intl402/DateTimeFormat/prototype/formatToParts/temporal-objects-resolved-time-zone.js
new file mode 100644
index 00000000000..92643905464
--- /dev/null
+++ b/test/intl402/DateTimeFormat/prototype/formatToParts/temporal-objects-resolved-time-zone.js
@@ -0,0 +1,96 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-Intl.DateTimeFormat.prototype.formatToParts
+description: A time zone in resolvedOptions with a large offset still produces the correct string
+locale: [en]
+includes: [deepEqual.js]
+features: [Temporal]
+---*/
+
+const formatter = new Intl.DateTimeFormat("en", { timeZone: "Pacific/Apia" });
+
+const date = new Temporal.PlainDate(2021, 8, 4);
+const dateResult = formatter.formatToParts(date);
+assert.deepEqual(dateResult, [
+ { type: "month", value: "8" },
+ { type: "literal", value: "/" },
+ { type: "day", value: "4" },
+ { type: "literal", value: "/" },
+ { type: "year", value: "2021" },
+], "plain date");
+
+const datetime1 = new Temporal.PlainDateTime(2021, 8, 4, 0, 30, 45, 123, 456, 789);
+const datetimeResult1 = formatter.formatToParts(datetime1);
+assert.deepEqual(datetimeResult1, [
+ { type: "month", value: "8" },
+ { type: "literal", value: "/" },
+ { type: "day", value: "4" },
+ { type: "literal", value: "/" },
+ { type: "year", value: "2021" },
+ { type: "literal", value: ", " },
+ { type: "hour", value: "12" },
+ { type: "literal", value: ":" },
+ { type: "minute", value: "30" },
+ { type: "literal", value: ":" },
+ { type: "second", value: "45" },
+ { type: "literal", value: " " },
+ { type: "dayPeriod", value: "AM" },
+], "plain datetime close to beginning of day");
+const datetime2 = new Temporal.PlainDateTime(2021, 8, 4, 23, 30, 45, 123, 456, 789);
+const datetimeResult2 = formatter.formatToParts(datetime2);
+assert.deepEqual(datetimeResult2, [
+ { type: "month", value: "8" },
+ { type: "literal", value: "/" },
+ { type: "day", value: "4" },
+ { type: "literal", value: "/" },
+ { type: "year", value: "2021" },
+ { type: "literal", value: ", " },
+ { type: "hour", value: "11" },
+ { type: "literal", value: ":" },
+ { type: "minute", value: "30" },
+ { type: "literal", value: ":" },
+ { type: "second", value: "45" },
+ { type: "literal", value: " " },
+ { type: "dayPeriod", value: "PM" },
+], "plain datetime close to end of day");
+
+const monthDay = new Temporal.PlainMonthDay(8, 4, "gregory");
+const monthDayResult = formatter.formatToParts(monthDay);
+assert.deepEqual(monthDayResult, [
+ { type: "month", value: "8" },
+ { type: "literal", value: "/" },
+ { type: "day", value: "4" },
+], "plain month-day");
+
+const time1 = new Temporal.PlainTime(0, 30, 45, 123, 456, 789);
+const timeResult1 = formatter.formatToParts(time1);
+assert.deepEqual(timeResult1, [
+ { type: "hour", value: "12" },
+ { type: "literal", value: ":" },
+ { type: "minute", value: "30" },
+ { type: "literal", value: ":" },
+ { type: "second", value: "45" },
+ { type: "literal", value: " " },
+ { type: "dayPeriod", value: "AM" },
+], "plain time close to beginning of day");
+const time2 = new Temporal.PlainTime(23, 30, 45, 123, 456, 789);
+const timeResult2 = formatter.formatToParts(time2);
+assert.deepEqual(timeResult2, [
+ { type: "hour", value: "11" },
+ { type: "literal", value: ":" },
+ { type: "minute", value: "30" },
+ { type: "literal", value: ":" },
+ { type: "second", value: "45" },
+ { type: "literal", value: " " },
+ { type: "dayPeriod", value: "PM" },
+], "plain time close to end of day");
+
+const month = new Temporal.PlainYearMonth(2021, 8, "gregory");
+const monthResult = formatter.formatToParts(month);
+assert.deepEqual(monthResult, [
+ { type: "month", value: "8" },
+ { type: "literal", value: "/" },
+ { type: "year", value: "2021" },
+], "plain year-month");
diff --git a/test/intl402/Temporal/Calendar/from/basic.js b/test/intl402/Temporal/Calendar/from/basic.js
new file mode 100644
index 00000000000..400a8911ed6
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/from/basic.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.from
+description: Support for non-ISO calendars in Calendar.from().
+features: [Temporal]
+---*/
+
+function test(item, id = item) {
+ const calendar = Temporal.Calendar.from(item);
+ assert(calendar instanceof Temporal.Calendar, `Calendar.from(${item}) is a calendar`);
+ assert.sameValue(calendar.id, id, `Calendar.from(${item}) has the correct ID`);
+}
+test("gregory");
+test("japanese");
+test("1994-11-05T08:15:30-05:00[u-ca=gregory]", "gregory");
+test("1994-11-05T13:15:30Z[u-ca=japanese]", "japanese");
diff --git a/test/intl402/Temporal/Calendar/prototype/dateAdd/date-infinity-throws-rangeerror.js b/test/intl402/Temporal/Calendar/prototype/dateAdd/date-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..238d6845302
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/dateAdd/date-infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.dateadd
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("gregory");
+const duration = new Temporal.Duration(1);
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.dateAdd({ era: "ad", eraYear: inf, month: 5, day: 2, calendar: instance }, duration, { overflow }), `eraYear property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.dateAdd({ era: "ad", eraYear: obj, month: 5, day: 2, calendar: instance }, duration, { overflow }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/dateFromFields/infinity-throws-rangeerror.js b/test/intl402/Temporal/Calendar/prototype/dateFromFields/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..94d8a0e6abb
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/dateFromFields/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.datefromfields
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.dateFromFields({ ...base, eraYear: inf }, { overflow }), `eraYear property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.dateFromFields({ ...base, eraYear: obj }, { overflow }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/dateUntil/argument-infinity-throws-rangeerror.js b/test/intl402/Temporal/Calendar/prototype/dateUntil/argument-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..5c836456b2a
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/dateUntil/argument-infinity-throws-rangeerror.js
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in a property bag for either argument is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.dateuntil
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("gregory");
+const other = new Temporal.PlainDate(2001, 6, 3);
+const base = { era: "ad", month: 5, day: 2, calendar: instance };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.dateUntil({ ...base, eraYear: inf }, other), `eraYear property cannot be ${inf}`);
+
+ assert.throws(RangeError, () => instance.dateUntil(other, { ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls1 = [];
+ const obj1 = TemporalHelpers.toPrimitiveObserver(calls1, inf, "eraYear");
+ assert.throws(RangeError, () => instance.dateUntil({ ...base, eraYear: obj1 }, other));
+ assert.compareArray(calls1, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+
+ const calls2 = [];
+ const obj2 = TemporalHelpers.toPrimitiveObserver(calls2, inf, "eraYear");
+ assert.throws(RangeError, () => instance.dateUntil(other, { ...base, eraYear: obj2 }));
+ assert.compareArray(calls2, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/day/infinity-throws-rangeerror.js b/test/intl402/Temporal/Calendar/prototype/day/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..d4ce585df76
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/day/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.day
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.day({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.day({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/dayOfWeek/infinity-throws-rangeerror.js b/test/intl402/Temporal/Calendar/prototype/dayOfWeek/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..d85e547f052
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/dayOfWeek/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.dayofweek
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.dayOfWeek({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.dayOfWeek({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/dayOfYear/infinity-throws-rangeerror.js b/test/intl402/Temporal/Calendar/prototype/dayOfYear/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..a8843191669
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/dayOfYear/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.dayofyear
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.dayOfYear({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.dayOfYear({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/daysInMonth/infinity-throws-rangeerror.js b/test/intl402/Temporal/Calendar/prototype/daysInMonth/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..c39c875c41f
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/daysInMonth/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.daysinmonth
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.daysInMonth({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.daysInMonth({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/daysInWeek/infinity-throws-rangeerror.js b/test/intl402/Temporal/Calendar/prototype/daysInWeek/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..d192101be13
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/daysInWeek/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.daysinweek
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.daysInWeek({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.daysInWeek({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/daysInYear/infinity-throws-rangeerror.js b/test/intl402/Temporal/Calendar/prototype/daysInYear/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..7f60f72328e
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/daysInYear/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.daysinyear
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.daysInYear({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.daysInYear({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/era/builtin.js b/test/intl402/Temporal/Calendar/prototype/era/builtin.js
new file mode 100644
index 00000000000..f51e65a3970
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/era/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.era
+description: >
+ Tests that Temporal.Calendar.prototype.era
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.era),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.era),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.era),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.era.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/intl402/Temporal/Calendar/prototype/era/infinity-throws-rangeerror.js b/test/intl402/Temporal/Calendar/prototype/era/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..a7a27dde31b
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/era/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.era
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.era({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.era({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/era/length.js b/test/intl402/Temporal/Calendar/prototype/era/length.js
new file mode 100644
index 00000000000..622daa6779c
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/era/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.era
+description: Temporal.Calendar.prototype.era.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.era, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/era/name.js b/test/intl402/Temporal/Calendar/prototype/era/name.js
new file mode 100644
index 00000000000..a188e02d844
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/era/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.era
+description: Temporal.Calendar.prototype.era.name is "era".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.era, "name", {
+ value: "era",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/era/not-a-constructor.js b/test/intl402/Temporal/Calendar/prototype/era/not-a-constructor.js
new file mode 100644
index 00000000000..41c0c40829e
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/era/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.era
+description: >
+ Temporal.Calendar.prototype.era does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.era();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.era), false,
+ "isConstructor(Temporal.Calendar.prototype.era)");
diff --git a/test/intl402/Temporal/Calendar/prototype/era/prop-desc.js b/test/intl402/Temporal/Calendar/prototype/era/prop-desc.js
new file mode 100644
index 00000000000..5093e9bad74
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/era/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.era
+description: The "era" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.era,
+ "function",
+ "`typeof Calendar.prototype.era` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "era", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/eraYear/builtin.js b/test/intl402/Temporal/Calendar/prototype/eraYear/builtin.js
new file mode 100644
index 00000000000..234fe88aea9
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/eraYear/builtin.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.erayear
+description: >
+ Tests that Temporal.Calendar.prototype.eraYear
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.Calendar.prototype.eraYear),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.Calendar.prototype.eraYear),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.Calendar.prototype.eraYear),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.Calendar.prototype.eraYear.hasOwnProperty("prototype"),
+ false, "prototype property");
diff --git a/test/intl402/Temporal/Calendar/prototype/eraYear/infinity-throws-rangeerror.js b/test/intl402/Temporal/Calendar/prototype/eraYear/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..1d20c67e7ba
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/eraYear/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.erayear
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.eraYear({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.eraYear({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/eraYear/length.js b/test/intl402/Temporal/Calendar/prototype/eraYear/length.js
new file mode 100644
index 00000000000..b65243ef4ba
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/eraYear/length.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.erayear
+description: Temporal.Calendar.prototype.eraYear.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.eraYear, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/eraYear/name.js b/test/intl402/Temporal/Calendar/prototype/eraYear/name.js
new file mode 100644
index 00000000000..dc9c0ffde5c
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/eraYear/name.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.erayear
+description: Temporal.Calendar.prototype.eraYear.name is "eraYear".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.Calendar.prototype.eraYear, "name", {
+ value: "eraYear",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/eraYear/not-a-constructor.js b/test/intl402/Temporal/Calendar/prototype/eraYear/not-a-constructor.js
new file mode 100644
index 00000000000..a227a177d11
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/eraYear/not-a-constructor.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.erayear
+description: >
+ Temporal.Calendar.prototype.eraYear does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.Calendar.prototype.eraYear();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.Calendar.prototype.eraYear), false,
+ "isConstructor(Temporal.Calendar.prototype.eraYear)");
diff --git a/test/intl402/Temporal/Calendar/prototype/eraYear/prop-desc.js b/test/intl402/Temporal/Calendar/prototype/eraYear/prop-desc.js
new file mode 100644
index 00000000000..9d857298554
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/eraYear/prop-desc.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.calendar.prototype.erayear
+description: The "eraYear" property of Temporal.Calendar.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.Calendar.prototype.eraYear,
+ "function",
+ "`typeof Calendar.prototype.eraYear` is `function`"
+);
+
+verifyProperty(Temporal.Calendar.prototype, "eraYear", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/inLeapYear/infinity-throws-rangeerror.js b/test/intl402/Temporal/Calendar/prototype/inLeapYear/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..7b35c962818
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/inLeapYear/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.inleapyear
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.inLeapYear({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.inLeapYear({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/month/infinity-throws-rangeerror.js b/test/intl402/Temporal/Calendar/prototype/month/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..6303b19aa81
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/month/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.month
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.month({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.month({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/monthCode/infinity-throws-rangeerror.js b/test/intl402/Temporal/Calendar/prototype/monthCode/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..0723a48a3be
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/monthCode/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.monthcode
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.monthCode({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.monthCode({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/monthDayFromFields/infinity-throws-rangeerror.js b/test/intl402/Temporal/Calendar/prototype/monthDayFromFields/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..d25aed49656
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/monthDayFromFields/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.monthdayfromfields
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.monthDayFromFields({ ...base, eraYear: inf }, { overflow }), `eraYear property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.monthDayFromFields({ ...base, eraYear: obj }, { overflow }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/monthsInYear/infinity-throws-rangeerror.js b/test/intl402/Temporal/Calendar/prototype/monthsInYear/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..20f63ef9dc3
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/monthsInYear/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.monthsinyear
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.monthsInYear({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.monthsInYear({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/weekOfYear/infinity-throws-rangeerror.js b/test/intl402/Temporal/Calendar/prototype/weekOfYear/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..63b892aa862
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/weekOfYear/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.weekofyear
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.weekOfYear({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.weekOfYear({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/year/infinity-throws-rangeerror.js b/test/intl402/Temporal/Calendar/prototype/year/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..0993a4ec332
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/year/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.year
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.year({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.year({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/Calendar/prototype/yearMonthFromFields/infinity-throws-rangeerror.js b/test/intl402/Temporal/Calendar/prototype/yearMonthFromFields/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..d883bb2b9a6
--- /dev/null
+++ b/test/intl402/Temporal/Calendar/prototype/yearMonthFromFields/infinity-throws-rangeerror.js
@@ -0,0 +1,23 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.calendar.prototype.yearmonthfromfields
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Calendar("gregory");
+const base = { era: "ad", month: 5, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => instance.yearMonthFromFields({ ...base, eraYear: inf }, { overflow }), `eraYear property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.yearMonthFromFields({ ...base, eraYear: obj }, { overflow }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/intl402/Temporal/Duration/prototype/add/relativeto-infinity-throws-rangeerror.js b/test/intl402/Temporal/Duration/prototype/add/relativeto-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..d2b0b866cef
--- /dev/null
+++ b/test/intl402/Temporal/Duration/prototype/add/relativeto-infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.duration.prototype.add
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const base = { era: "ad", month: 5, day: 2, hour: 15, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.add(instance, { relativeTo: { ...base, eraYear: inf } }), `eraYear property cannot be ${inf} in relativeTo`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.add(instance, { relativeTo: { ...base, eraYear: obj } }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/Duration/prototype/round/relativeto-infinity-throws-rangeerror.js b/test/intl402/Temporal/Duration/prototype/round/relativeto-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..b55a16228aa
--- /dev/null
+++ b/test/intl402/Temporal/Duration/prototype/round/relativeto-infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.duration.prototype.round
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const base = { era: "ad", month: 5, day: 2, hour: 15, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.round({ smallestUnit: "seconds", relativeTo: { ...base, eraYear: inf } }), `eraYear property cannot be ${inf} in relativeTo`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.round({ smallestUnit: "seconds", relativeTo: { ...base, eraYear: obj } }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/Duration/prototype/subtract/relativeto-infinity-throws-rangeerror.js b/test/intl402/Temporal/Duration/prototype/subtract/relativeto-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..75d223b85ed
--- /dev/null
+++ b/test/intl402/Temporal/Duration/prototype/subtract/relativeto-infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.duration.prototype.subtract
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const base = { era: "ad", month: 5, day: 2, hour: 15, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.subtract(instance, { relativeTo: { ...base, eraYear: inf } }), `eraYear property cannot be ${inf} in relativeTo`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.subtract(instance, { relativeTo: { ...base, eraYear: obj } }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/Duration/prototype/total/relativeto-infinity-throws-rangeerror.js b/test/intl402/Temporal/Duration/prototype/total/relativeto-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..9fa5335ff41
--- /dev/null
+++ b/test/intl402/Temporal/Duration/prototype/total/relativeto-infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.duration.prototype.total
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
+const base = { era: "ad", month: 5, day: 2, hour: 15, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.total({ unit: "seconds", relativeTo: { ...base, eraYear: inf } }), `eraYear property cannot be ${inf} in relativeTo`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.total({ unit: "seconds", relativeTo: { ...base, eraYear: obj } }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/PlainDate/compare/infinity-throws-rangeerror.js b/test/intl402/Temporal/PlainDate/compare/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..5912c52e5a0
--- /dev/null
+++ b/test/intl402/Temporal/PlainDate/compare/infinity-throws-rangeerror.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in a property bag for either argument is Infinity or -Infinity
+esid: sec-temporal.plaindate.compare
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const other = new Temporal.PlainDate(2000, 5, 2, "gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => Temporal.PlainDate.compare({ ...base, eraYear: inf }, other), `eraYear property cannot be ${inf}`);
+
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(other, { ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls1 = [];
+ const obj1 = TemporalHelpers.toPrimitiveObserver(calls1, inf, "eraYear");
+ assert.throws(RangeError, () => Temporal.PlainDate.compare({ ...base, eraYear: obj1 }, other));
+ assert.compareArray(calls1, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+
+ const calls2 = [];
+ const obj2 = TemporalHelpers.toPrimitiveObserver(calls2, inf, "eraYear");
+ assert.throws(RangeError, () => Temporal.PlainDate.compare(other, { ...base, eraYear: obj2 }));
+ assert.compareArray(calls2, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/PlainDate/from/basic.js b/test/intl402/Temporal/PlainDate/from/basic.js
new file mode 100644
index 00000000000..c12cab6e14d
--- /dev/null
+++ b/test/intl402/Temporal/PlainDate/from/basic.js
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.from
+description: Basic tests for PlainDate.from() with the Gregorian calendar.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+TemporalHelpers.assertPlainDate(
+ Temporal.PlainDate.from("1999-12-31[u-ca=gregory]"),
+ 1999, 12, "M12", 31, "string era CE", "ce", 1999);
+
+TemporalHelpers.assertPlainDate(
+ Temporal.PlainDate.from("-000001-12-31[u-ca=gregory]"),
+ -1, 12, "M12", 31, "string era BCE", "bce", 2);
+
+TemporalHelpers.assertPlainDate(
+ Temporal.PlainDate.from({ era: "ce", eraYear: 1999, month: 12, day: 31, calendar: "gregory" }),
+ 1999, 12, "M12", 31, "property bag explicit era CE", "ce", 1999);
+
+TemporalHelpers.assertPlainDate(
+ Temporal.PlainDate.from({ year: 1999, month: 12, day: 31, calendar: "gregory" }),
+ 1999, 12, "M12", 31, "property bag implicit era CE", "ce", 1999);
+
+TemporalHelpers.assertPlainDate(
+ Temporal.PlainDate.from({ era: "bce", eraYear: 2, month: 12, day: 31, calendar: "gregory" }),
+ -1, 12, "M12", 31, "property bag era BCE", "bce", 2);
diff --git a/test/intl402/Temporal/PlainDate/from/infinity-throws-rangeerror.js b/test/intl402/Temporal/PlainDate/from/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..8f0efdcafdf
--- /dev/null
+++ b/test/intl402/Temporal/PlainDate/from/infinity-throws-rangeerror.js
@@ -0,0 +1,22 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindate.from
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => Temporal.PlainDate.from({ ...base, eraYear: inf }, { overflow }), `eraYear property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => Temporal.PlainDate.from({ ...base, eraYear: obj }, { overflow }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/intl402/Temporal/PlainDate/prototype/equals/infinity-throws-rangeerror.js b/test/intl402/Temporal/PlainDate/prototype/equals/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..8b8c3fbac81
--- /dev/null
+++ b/test/intl402/Temporal/PlainDate/prototype/equals/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindate.prototype.equals
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.equals({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.equals({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/PlainDate/prototype/era/prop-desc.js b/test/intl402/Temporal/PlainDate/prototype/era/prop-desc.js
new file mode 100644
index 00000000000..8bffec1dd5c
--- /dev/null
+++ b/test/intl402/Temporal/PlainDate/prototype/era/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.era
+description: The "era" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "era");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/intl402/Temporal/PlainDate/prototype/eraYear/calendar-returns-infinity.js b/test/intl402/Temporal/PlainDate/prototype/eraYear/calendar-returns-infinity.js
new file mode 100644
index 00000000000..cd143556b0e
--- /dev/null
+++ b/test/intl402/Temporal/PlainDate/prototype/eraYear/calendar-returns-infinity.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.erayear
+description: Getter throws if the calendar returns ±∞ from its eraYear method
+features: [Temporal]
+---*/
+
+class InfinityCalendar extends Temporal.Calendar {
+ constructor(positive) {
+ super("iso8601");
+ this.positive = positive;
+ }
+
+ eraYear() {
+ return this.positive ? Infinity : -Infinity;
+ }
+}
+
+const pos = new InfinityCalendar(true);
+const instance1 = new Temporal.PlainDate(2000, 5, 2, pos);
+assert.throws(RangeError, () => instance1.eraYear);
+
+const neg = new InfinityCalendar(false);
+const instance2 = new Temporal.PlainDate(2000, 5, 2, neg);
+assert.throws(RangeError, () => instance2.eraYear);
diff --git a/test/intl402/Temporal/PlainDate/prototype/eraYear/prop-desc.js b/test/intl402/Temporal/PlainDate/prototype/eraYear/prop-desc.js
new file mode 100644
index 00000000000..f1a182524a7
--- /dev/null
+++ b/test/intl402/Temporal/PlainDate/prototype/eraYear/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindate.prototype.erayear
+description: The "eraYear" property of Temporal.PlainDate.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDate.prototype, "eraYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/intl402/Temporal/PlainDate/prototype/since/infinity-throws-rangeerror.js b/test/intl402/Temporal/PlainDate/prototype/since/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..ca956787072
--- /dev/null
+++ b/test/intl402/Temporal/PlainDate/prototype/since/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindate.prototype.since
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.since({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.since({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/PlainDate/prototype/toLocaleString/resolved-time-zone.js b/test/intl402/Temporal/PlainDate/prototype/toLocaleString/resolved-time-zone.js
new file mode 100644
index 00000000000..6287b4baf0e
--- /dev/null
+++ b/test/intl402/Temporal/PlainDate/prototype/toLocaleString/resolved-time-zone.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindate.prototype.tolocalestring
+description: A time zone in resolvedOptions with a large offset still produces the correct string
+locale: [en]
+features: [Temporal]
+---*/
+
+const date = new Temporal.PlainDate(2021, 8, 4);
+const result = date.toLocaleString("en", { timeZone: "Pacific/Apia" });
+assert.sameValue(result, "8/4/2021");
diff --git a/test/intl402/Temporal/PlainDate/prototype/until/infinity-throws-rangeerror.js b/test/intl402/Temporal/PlainDate/prototype/until/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..7eaae75ee97
--- /dev/null
+++ b/test/intl402/Temporal/PlainDate/prototype/until/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindate.prototype.until
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDate(2000, 5, 2, "gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.until({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.until({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/PlainDateTime/compare/infinity-throws-rangeerror.js b/test/intl402/Temporal/PlainDateTime/compare/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..ea0e364760a
--- /dev/null
+++ b/test/intl402/Temporal/PlainDateTime/compare/infinity-throws-rangeerror.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in a property bag for either argument is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.compare
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const other = new Temporal.PlainDateTime(2000, 5, 2, 15, "gregory");
+const base = { era: "ad", month: 5, day: 2, hour: 15, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare({ ...base, eraYear: inf }, other), `eraYear property cannot be ${inf}`);
+
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(other, { ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls1 = [];
+ const obj1 = TemporalHelpers.toPrimitiveObserver(calls1, inf, "eraYear");
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare({ ...base, eraYear: obj1 }, other));
+ assert.compareArray(calls1, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+
+ const calls2 = [];
+ const obj2 = TemporalHelpers.toPrimitiveObserver(calls2, inf, "eraYear");
+ assert.throws(RangeError, () => Temporal.PlainDateTime.compare(other, { ...base, eraYear: obj2 }));
+ assert.compareArray(calls2, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/PlainDateTime/from/infinity-throws-rangeerror.js b/test/intl402/Temporal/PlainDateTime/from/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..ead46deba60
--- /dev/null
+++ b/test/intl402/Temporal/PlainDateTime/from/infinity-throws-rangeerror.js
@@ -0,0 +1,22 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.from
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const base = { era: "ad", month: 5, day: 2, hour: 15, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => Temporal.PlainDateTime.from({ ...base, eraYear: inf }, { overflow }), `eraYear property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => Temporal.PlainDateTime.from({ ...base, eraYear: obj }, { overflow }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/intl402/Temporal/PlainDateTime/prototype/equals/infinity-throws-rangeerror.js b/test/intl402/Temporal/PlainDateTime/prototype/equals/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..3760a573917
--- /dev/null
+++ b/test/intl402/Temporal/PlainDateTime/prototype/equals/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.prototype.equals
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, "gregory");
+const base = { era: "ad", month: 5, day: 2, hour: 15, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.equals({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.equals({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/PlainDateTime/prototype/era/prop-desc.js b/test/intl402/Temporal/PlainDateTime/prototype/era/prop-desc.js
new file mode 100644
index 00000000000..5247bc3f95f
--- /dev/null
+++ b/test/intl402/Temporal/PlainDateTime/prototype/era/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.era
+description: The "era" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "era");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/intl402/Temporal/PlainDateTime/prototype/eraYear/calendar-returns-infinity.js b/test/intl402/Temporal/PlainDateTime/prototype/eraYear/calendar-returns-infinity.js
new file mode 100644
index 00000000000..0655d8ee9a9
--- /dev/null
+++ b/test/intl402/Temporal/PlainDateTime/prototype/eraYear/calendar-returns-infinity.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.erayear
+description: Getter throws if the calendar returns ±∞ from its eraYear method
+features: [Temporal]
+---*/
+
+class InfinityCalendar extends Temporal.Calendar {
+ constructor(positive) {
+ super("iso8601");
+ this.positive = positive;
+ }
+
+ eraYear() {
+ return this.positive ? Infinity : -Infinity;
+ }
+}
+
+const pos = new InfinityCalendar(true);
+const instance1 = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321, pos);
+assert.throws(RangeError, () => instance1.eraYear);
+
+const neg = new InfinityCalendar(false);
+const instance2 = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321, neg);
+assert.throws(RangeError, () => instance2.eraYear);
diff --git a/test/intl402/Temporal/PlainDateTime/prototype/eraYear/prop-desc.js b/test/intl402/Temporal/PlainDateTime/prototype/eraYear/prop-desc.js
new file mode 100644
index 00000000000..573a14ce17c
--- /dev/null
+++ b/test/intl402/Temporal/PlainDateTime/prototype/eraYear/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plaindatetime.prototype.erayear
+description: The "eraYear" property of Temporal.PlainDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainDateTime.prototype, "eraYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/intl402/Temporal/PlainDateTime/prototype/since/infinity-throws-rangeerror.js b/test/intl402/Temporal/PlainDateTime/prototype/since/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..10e5b7d17e0
--- /dev/null
+++ b/test/intl402/Temporal/PlainDateTime/prototype/since/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.prototype.since
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, "gregory");
+const base = { era: "ad", month: 5, day: 2, hour: 15, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.since({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.since({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/PlainDateTime/prototype/toLocaleString/resolved-time-zone.js b/test/intl402/Temporal/PlainDateTime/prototype/toLocaleString/resolved-time-zone.js
new file mode 100644
index 00000000000..c3f1aaa0171
--- /dev/null
+++ b/test/intl402/Temporal/PlainDateTime/prototype/toLocaleString/resolved-time-zone.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaindatetime.prototype.tolocalestring
+description: A time zone in resolvedOptions with a large offset still produces the correct string
+locale: [en]
+features: [Temporal]
+---*/
+
+const datetime1 = new Temporal.PlainDateTime(2021, 8, 4, 0, 30, 45, 123, 456, 789);
+const result1 = datetime1.toLocaleString("en", { timeZone: "Pacific/Apia" });
+assert.sameValue(result1, "8/4/2021, 12:30:45 AM");
+
+const datetime2 = new Temporal.PlainDateTime(2021, 8, 4, 23, 30, 45, 123, 456, 789);
+const result2 = datetime2.toLocaleString("en", { timeZone: "Pacific/Apia" });
+assert.sameValue(result2, "8/4/2021, 11:30:45 PM");
diff --git a/test/intl402/Temporal/PlainDateTime/prototype/until/infinity-throws-rangeerror.js b/test/intl402/Temporal/PlainDateTime/prototype/until/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..537274e1bb1
--- /dev/null
+++ b/test/intl402/Temporal/PlainDateTime/prototype/until/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.prototype.until
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, "gregory");
+const base = { era: "ad", month: 5, day: 2, hour: 15, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.until({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.until({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/PlainDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js b/test/intl402/Temporal/PlainDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..a6c7f88d5bb
--- /dev/null
+++ b/test/intl402/Temporal/PlainDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.plaindatetime.prototype.withplaindate
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, "gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.withPlainDate({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.withPlainDate({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/PlainMonthDay/from/basic.js b/test/intl402/Temporal/PlainMonthDay/from/basic.js
new file mode 100644
index 00000000000..d4e09219d7e
--- /dev/null
+++ b/test/intl402/Temporal/PlainMonthDay/from/basic.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.from
+description: Basic tests for PlainMonthDay.from() with the Gregorian calendar.
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const tests = [
+ { era: "ce", eraYear: 2019, month: 11, day: 18, calendar: "gregory" },
+ { era: "ce", eraYear: 1979, month: 11, day: 18, calendar: "gregory" },
+ { era: "ce", eraYear: 2019, monthCode: "M11", day: 18, calendar: "gregory" },
+ { era: "ce", eraYear: 1979, monthCode: "M11", day: 18, calendar: "gregory" },
+ { year: 1970, month: 11, day: 18, calendar: "gregory" },
+ { era: "ce", eraYear: 1970, month: 11, day: 18, calendar: "gregory" },
+];
+
+for (const fields of tests) {
+ const result = Temporal.PlainMonthDay.from(fields);
+ TemporalHelpers.assertPlainMonthDay(result, "M11", 18);
+ assert.sameValue(result.calendar.id, "gregory");
+ assert.sameValue(result.toString(), "1972-11-18[u-ca=gregory]");
+}
+
+assert.throws(TypeError, () => Temporal.PlainMonthDay.from({ month: 11, day: 18, calendar: "gregory" }));
diff --git a/test/intl402/Temporal/PlainMonthDay/prototype/equals/infinity-throws-rangeerror.js b/test/intl402/Temporal/PlainMonthDay/prototype/equals/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..efb8cbf4dd4
--- /dev/null
+++ b/test/intl402/Temporal/PlainMonthDay/prototype/equals/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainmonthday.prototype.equals
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(5, 2, "gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.equals({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.equals({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/PlainMonthDay/prototype/toLocaleString/resolved-time-zone.js b/test/intl402/Temporal/PlainMonthDay/prototype/toLocaleString/resolved-time-zone.js
new file mode 100644
index 00000000000..a8b7a4091a9
--- /dev/null
+++ b/test/intl402/Temporal/PlainMonthDay/prototype/toLocaleString/resolved-time-zone.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainmonthday.prototype.tolocalestring
+description: A time zone in resolvedOptions with a large offset still produces the correct string
+locale: [en]
+features: [Temporal]
+---*/
+
+const monthDay = new Temporal.PlainMonthDay(8, 4, "gregory");
+const result = monthDay.toLocaleString("en", { timeZone: "Pacific/Apia" });
+assert.sameValue(result, "8/4");
diff --git a/test/intl402/Temporal/PlainMonthDay/prototype/toPlainDate/infinity-throws-rangeerror.js b/test/intl402/Temporal/PlainMonthDay/prototype/toPlainDate/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..1ab5ed99377
--- /dev/null
+++ b/test/intl402/Temporal/PlainMonthDay/prototype/toPlainDate/infinity-throws-rangeerror.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws a RangeError if eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainmonthday.prototype.toplaindate
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainMonthDay(5, 2, "gregory");
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.toPlainDate({ era: "ad", eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.toPlainDate({ era: "ad", eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/PlainTime/prototype/toLocaleString/resolved-time-zone.js b/test/intl402/Temporal/PlainTime/prototype/toLocaleString/resolved-time-zone.js
new file mode 100644
index 00000000000..55227b799cf
--- /dev/null
+++ b/test/intl402/Temporal/PlainTime/prototype/toLocaleString/resolved-time-zone.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 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: A time zone in resolvedOptions with a large offset still produces the correct string
+locale: [en]
+features: [Temporal]
+---*/
+
+const time1 = new Temporal.PlainTime(0, 30, 45, 123, 456, 789);
+const result1 = time1.toLocaleString("en", { timeZone: "Pacific/Apia" });
+assert.sameValue(result1, "12:30:45 AM");
+
+const time2 = new Temporal.PlainTime(23, 30, 45, 123, 456, 789);
+const result2 = time2.toLocaleString("en", { timeZone: "Pacific/Apia" });
+assert.sameValue(result2, "11:30:45 PM");
diff --git a/test/intl402/Temporal/PlainTime/prototype/toPlainDateTime/infinity-throws-rangeerror.js b/test/intl402/Temporal/PlainTime/prototype/toPlainDateTime/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..5b858b9da5c
--- /dev/null
+++ b/test/intl402/Temporal/PlainTime/prototype/toPlainDateTime/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear 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 = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.toPlainDateTime({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.toPlainDateTime({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/PlainTime/prototype/toZonedDateTime/plaindate-infinity-throws-rangeerror.js b/test/intl402/Temporal/PlainTime/prototype/toZonedDateTime/plaindate-infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..81aa6d8f722
--- /dev/null
+++ b/test/intl402/Temporal/PlainTime/prototype/toZonedDateTime/plaindate-infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear 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 = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.toZonedDateTime({ timeZone: "UTC", plainDate: { ...base, eraYear: inf } }), `eraYear property cannot be ${inf} in plainDate`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.toZonedDateTime({ timeZone: "UTC", plainDate: { ...base, eraYear: obj } }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/PlainYearMonth/compare/infinity-throws-rangeerror.js b/test/intl402/Temporal/PlainYearMonth/compare/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..c881327a04a
--- /dev/null
+++ b/test/intl402/Temporal/PlainYearMonth/compare/infinity-throws-rangeerror.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in a property bag for either argument is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.compare
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const other = new Temporal.PlainYearMonth(2000, 5, "gregory");
+const base = { era: "ad", month: 5, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare({ ...base, eraYear: inf }, other), `eraYear property cannot be ${inf}`);
+
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(other, { ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls1 = [];
+ const obj1 = TemporalHelpers.toPrimitiveObserver(calls1, inf, "eraYear");
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare({ ...base, eraYear: obj1 }, other));
+ assert.compareArray(calls1, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+
+ const calls2 = [];
+ const obj2 = TemporalHelpers.toPrimitiveObserver(calls2, inf, "eraYear");
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.compare(other, { ...base, eraYear: obj2 }));
+ assert.compareArray(calls2, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/PlainYearMonth/from/infinity-throws-rangeerror.js b/test/intl402/Temporal/PlainYearMonth/from/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..22594e69b6b
--- /dev/null
+++ b/test/intl402/Temporal/PlainYearMonth/from/infinity-throws-rangeerror.js
@@ -0,0 +1,22 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.from
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const base = { era: "ad", month: 5, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ ...base, eraYear: inf }, { overflow }), `eraYear property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => Temporal.PlainYearMonth.from({ ...base, eraYear: obj }, { overflow }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/intl402/Temporal/PlainYearMonth/prototype/equals/infinity-throws-rangeerror.js b/test/intl402/Temporal/PlainYearMonth/prototype/equals/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..eaebc9b3978
--- /dev/null
+++ b/test/intl402/Temporal/PlainYearMonth/prototype/equals/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.prototype.equals
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "gregory");
+const base = { era: "ad", month: 5, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.equals({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.equals({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/PlainYearMonth/prototype/era/prop-desc.js b/test/intl402/Temporal/PlainYearMonth/prototype/era/prop-desc.js
new file mode 100644
index 00000000000..fd8c3ce0294
--- /dev/null
+++ b/test/intl402/Temporal/PlainYearMonth/prototype/era/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.era
+description: The "era" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "era");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/intl402/Temporal/PlainYearMonth/prototype/eraYear/calendar-returns-infinity.js b/test/intl402/Temporal/PlainYearMonth/prototype/eraYear/calendar-returns-infinity.js
new file mode 100644
index 00000000000..f8b96891153
--- /dev/null
+++ b/test/intl402/Temporal/PlainYearMonth/prototype/eraYear/calendar-returns-infinity.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.erayear
+description: Getter throws if the calendar returns ±∞ from its eraYear method
+features: [Temporal]
+---*/
+
+class InfinityCalendar extends Temporal.Calendar {
+ constructor(positive) {
+ super("iso8601");
+ this.positive = positive;
+ }
+
+ eraYear() {
+ return this.positive ? Infinity : -Infinity;
+ }
+}
+
+const pos = new InfinityCalendar(true);
+const instance1 = new Temporal.PlainYearMonth(2000, 5, pos);
+assert.throws(RangeError, () => instance1.eraYear);
+
+const neg = new InfinityCalendar(false);
+const instance2 = new Temporal.PlainYearMonth(2000, 5, neg);
+assert.throws(RangeError, () => instance2.eraYear);
diff --git a/test/intl402/Temporal/PlainYearMonth/prototype/eraYear/prop-desc.js b/test/intl402/Temporal/PlainYearMonth/prototype/eraYear/prop-desc.js
new file mode 100644
index 00000000000..131088c5fa0
--- /dev/null
+++ b/test/intl402/Temporal/PlainYearMonth/prototype/eraYear/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.plainyearmonth.prototype.erayear
+description: The "eraYear" property of Temporal.PlainYearMonth.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.PlainYearMonth.prototype, "eraYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/intl402/Temporal/PlainYearMonth/prototype/since/infinity-throws-rangeerror.js b/test/intl402/Temporal/PlainYearMonth/prototype/since/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..20e322bff88
--- /dev/null
+++ b/test/intl402/Temporal/PlainYearMonth/prototype/since/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.prototype.since
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "gregory");
+const base = { era: "ad", month: 5, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.since({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.since({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/PlainYearMonth/prototype/toLocaleString/resolved-time-zone.js b/test/intl402/Temporal/PlainYearMonth/prototype/toLocaleString/resolved-time-zone.js
new file mode 100644
index 00000000000..9e90adef382
--- /dev/null
+++ b/test/intl402/Temporal/PlainYearMonth/prototype/toLocaleString/resolved-time-zone.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plainyearmonth.prototype.tolocalestring
+description: A time zone in resolvedOptions with a large offset still produces the correct string
+locale: [en]
+features: [Temporal]
+---*/
+
+const month = new Temporal.PlainYearMonth(2021, 8, "gregory");
+const result = month.toLocaleString("en", { timeZone: "Pacific/Apia" });
+assert.sameValue(result, "8/2021");
diff --git a/test/intl402/Temporal/PlainYearMonth/prototype/until/infinity-throws-rangeerror.js b/test/intl402/Temporal/PlainYearMonth/prototype/until/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..9ee6e5fafad
--- /dev/null
+++ b/test/intl402/Temporal/PlainYearMonth/prototype/until/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.plainyearmonth.prototype.until
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainYearMonth(2000, 5, "gregory");
+const base = { era: "ad", month: 5, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.until({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.until({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/TimeZone/basic.js b/test/intl402/Temporal/TimeZone/basic.js
new file mode 100644
index 00000000000..08722358ae7
--- /dev/null
+++ b/test/intl402/Temporal/TimeZone/basic.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone
+description: Basic tests for the Temporal.TimeZone constructor.
+features: [Temporal]
+---*/
+
+const valid = [
+ ["Europe/Vienna"],
+ ["America/New_York"],
+ ["Africa/CAIRO", "Africa/Cairo"],
+ ["Asia/Ulan_Bator", "Asia/Ulaanbaatar"],
+ ["GMT", "UTC"],
+];
+for (const [zone, id = zone] of valid) {
+ const result = new Temporal.TimeZone(zone);
+ assert.sameValue(typeof result, "object", `object should be created for ${zone}`);
+ assert.sameValue(result.id, id, `id for ${zone} should be ${id}`);
+}
+
+const invalid = ["+00:01.1", "-01.1"];
+for (const zone of invalid) {
+ assert.throws(RangeError, () => new Temporal.TimeZone(zone), `should throw for ${zone}`);
+}
diff --git a/test/intl402/Temporal/TimeZone/from/argument-invalid.js b/test/intl402/Temporal/TimeZone/from/argument-invalid.js
new file mode 100644
index 00000000000..6462e306933
--- /dev/null
+++ b/test/intl402/Temporal/TimeZone/from/argument-invalid.js
@@ -0,0 +1,18 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.from
+description: TimeZone.from() with bad offsets in ISO strings.
+features: [Temporal]
+---*/
+
+const invalids = [
+ "1994-11-05T08:15:30-05:00[UTC]",
+ "1994-11-05T13:15:30+00:00[America/New_York]",
+ "1994-11-05T13:15:30-03[Europe/Brussels]",
+];
+
+for (const item of invalids) {
+ assert.throws(RangeError, () => Temporal.TimeZone.from(item), `Throws for ${item}`);
+}
diff --git a/test/intl402/Temporal/TimeZone/from/argument-object.js b/test/intl402/Temporal/TimeZone/from/argument-object.js
new file mode 100644
index 00000000000..7991c341bda
--- /dev/null
+++ b/test/intl402/Temporal/TimeZone/from/argument-object.js
@@ -0,0 +1,45 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.from
+description: An object is returned unchanged
+features: [Temporal]
+---*/
+
+class CustomTimeZone extends Temporal.TimeZone {}
+
+const objects = [
+ new Temporal.TimeZone("Europe/Madrid"),
+ new CustomTimeZone("Africa/Accra"),
+];
+
+const thisValues = [
+ Temporal.TimeZone,
+ CustomTimeZone,
+ {},
+ null,
+ undefined,
+ 7,
+];
+
+for (const thisValue of thisValues) {
+ for (const object of objects) {
+ const result = Temporal.TimeZone.from.call(thisValue, object);
+ assert.sameValue(result, object);
+ }
+
+ const zdt = new Temporal.ZonedDateTime(0n, "Africa/Cairo");
+ const fromZdt = Temporal.TimeZone.from.call(thisValue, zdt);
+ assert.sameValue(fromZdt, zdt.timeZone);
+ assert.sameValue(fromZdt.id, "Africa/Cairo");
+
+ const tz = new Temporal.TimeZone("Africa/Cairo");
+ const fromPropertyBagObject = Temporal.TimeZone.from.call(thisValue, { timeZone: tz });
+ assert.sameValue(fromPropertyBagObject, tz);
+ assert.sameValue(fromPropertyBagObject.id, "Africa/Cairo");
+
+ const fromPropertyBagString = Temporal.TimeZone.from.call(thisValue, { timeZone: "Africa/Cairo" });
+ assert(fromPropertyBagString instanceof Temporal.TimeZone);
+ assert.sameValue(fromPropertyBagString.id, "Africa/Cairo");
+}
diff --git a/test/intl402/Temporal/TimeZone/from/argument-valid.js b/test/intl402/Temporal/TimeZone/from/argument-valid.js
new file mode 100644
index 00000000000..69a0161fcf1
--- /dev/null
+++ b/test/intl402/Temporal/TimeZone/from/argument-valid.js
@@ -0,0 +1,30 @@
+// Copyright (C) 2020 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.from
+description: Built-in time zones are parsed correctly out of valid strings
+features: [Temporal]
+---*/
+
+const valids = [
+ ["Africa/Bissau"],
+ ["America/Belem"],
+ ["Europe/Vienna"],
+ ["America/New_York"],
+ ["Africa/CAIRO", "Africa/Cairo"],
+ ["Asia/Ulan_Bator", "Asia/Ulaanbaatar"],
+ ["GMT", "UTC"],
+ ["etc/gmt", "UTC"],
+ ["1994-11-05T08:15:30-05:00[America/New_York]", "America/New_York"],
+ ["1994-11-05T08:15:30-05[America/New_York]", "America/New_York"],
+ ["1994-11-05T08:15:30\u221205:00[America/New_York]", "America/New_York"],
+ ["1994-11-05T08:15:30\u221205[America/New_York]", "America/New_York"],
+];
+
+for (const [valid, canonical = valid] of valids) {
+ const result = Temporal.TimeZone.from(valid);
+ assert.sameValue(Object.getPrototypeOf(result), Temporal.TimeZone.prototype);
+ assert.sameValue(result.id, canonical);
+ assert.sameValue(result.toString(), canonical);
+}
diff --git a/test/intl402/Temporal/TimeZone/prototype/getInstantFor/infinity-throws-rangeerror.js b/test/intl402/Temporal/TimeZone/prototype/getInstantFor/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..d525a22fb0a
--- /dev/null
+++ b/test/intl402/Temporal/TimeZone/prototype/getInstantFor/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.timezone.prototype.getinstantfor
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+const base = { era: "ad", month: 5, day: 2, hour: 15, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.getInstantFor({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.getInstantFor({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/instant-string.js b/test/intl402/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/instant-string.js
new file mode 100644
index 00000000000..1635fc858a7
--- /dev/null
+++ b/test/intl402/Temporal/TimeZone/prototype/getOffsetNanosecondsFor/instant-string.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetnanosecondsfor
+description: Conversion of ISO date-time strings to Temporal.Instant instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("America/Vancouver");
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.getOffsetNanosecondsFor(str), "bare date-time string is not an instant");
+str = "1970-01-01T00:00[America/Vancouver]";
+assert.throws(RangeError, () => instance.getOffsetNanosecondsFor(str), "date-time + IANA annotation is not an instant");
+
+// The following are all valid strings so should not throw:
+
+const valids = [
+ "1970-01-01T00:00Z",
+ "1970-01-01T00:00+01:00",
+ "1970-01-01T00:00Z[America/Vancouver]",
+ "1970-01-01T00:00+01:00[America/Vancouver]",
+];
+for (const str of valids) {
+ const result = instance.getOffsetNanosecondsFor(str);
+ assert.sameValue(result, -28800e9);
+}
diff --git a/test/intl402/Temporal/TimeZone/prototype/getOffsetStringFor/instant-string.js b/test/intl402/Temporal/TimeZone/prototype/getOffsetStringFor/instant-string.js
new file mode 100644
index 00000000000..8fa65ff52d2
--- /dev/null
+++ b/test/intl402/Temporal/TimeZone/prototype/getOffsetStringFor/instant-string.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getoffsetstringfor
+description: Conversion of ISO date-time strings to Temporal.TimeZone instances
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("America/Vancouver");
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.getOffsetStringFor(str), "bare date-time string is not an instant");
+str = "1970-01-01T00:00[America/Vancouver]";
+assert.throws(RangeError, () => instance.getOffsetStringFor(str), "date-time + IANA annotation is not an instant");
+
+// The following are all valid strings so should not throw:
+
+const valids = [
+ "1970-01-01T00:00Z",
+ "1970-01-01T00:00+01:00",
+ "1970-01-01T00:00Z[America/Vancouver]",
+ "1970-01-01T00:00+01:00[America/Vancouver]",
+];
+for (const str of valids) {
+ const result = instance.getOffsetStringFor(str);
+ assert.sameValue(result, "-08:00");
+}
diff --git a/test/intl402/Temporal/TimeZone/prototype/getPlainDateTimeFor/instant-string.js b/test/intl402/Temporal/TimeZone/prototype/getPlainDateTimeFor/instant-string.js
new file mode 100644
index 00000000000..2c2973e72f2
--- /dev/null
+++ b/test/intl402/Temporal/TimeZone/prototype/getPlainDateTimeFor/instant-string.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.timezone.prototype.getplaindatetimefor
+description: Conversion of ISO date-time strings to Temporal.Instant instances
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("America/Vancouver");
+
+let str = "1970-01-01T00:00";
+assert.throws(RangeError, () => instance.getPlainDateTimeFor(str), "bare date-time string is not an instant");
+str = "1970-01-01T00:00[America/Vancouver]";
+assert.throws(RangeError, () => instance.getPlainDateTimeFor(str), "date-time + IANA annotation is not an instant");
+
+str = "1970-01-01T00:00Z";
+const result1 = instance.getPlainDateTimeFor(str);
+TemporalHelpers.assertPlainDateTime(result1, 1969, 12, "M12", 31, 16, 0, 0, 0, 0, 0, "date-time + Z preserves exact time");
+
+str = "1970-01-01T00:00+01:00";
+const result2 = instance.getPlainDateTimeFor(str);
+TemporalHelpers.assertPlainDateTime(result2, 1969, 12, "M12", 31, 15, 0, 0, 0, 0, 0, "date-time + offset preserves exact time with offset");
+
+str = "1970-01-01T00:00Z[America/Vancouver]";
+const result3 = instance.getPlainDateTimeFor(str);
+TemporalHelpers.assertPlainDateTime(result3, 1969, 12, "M12", 31, 16, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation ignores the IANA annotation");
+
+str = "1970-01-01T00:00+01:00[America/Vancouver]";
+const result4 = instance.getPlainDateTimeFor(str);
+TemporalHelpers.assertPlainDateTime(result4, 1969, 12, "M12", 31, 15, 0, 0, 0, 0, 0, "date-time + offset + IANA annotation ignores the IANA annotation");
diff --git a/test/intl402/Temporal/TimeZone/prototype/getPossibleInstantsFor/infinity-throws-rangeerror.js b/test/intl402/Temporal/TimeZone/prototype/getPossibleInstantsFor/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..d388f95092d
--- /dev/null
+++ b/test/intl402/Temporal/TimeZone/prototype/getPossibleInstantsFor/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.timezone.prototype.getpossibleinstantsfor
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.TimeZone("UTC");
+const base = { era: "ad", month: 5, day: 2, hour: 15, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.getPossibleInstantsFor({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.getPossibleInstantsFor({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/ZonedDateTime/compare/infinity-throws-rangeerror.js b/test/intl402/Temporal/ZonedDateTime/compare/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..32abe5a8465
--- /dev/null
+++ b/test/intl402/Temporal/ZonedDateTime/compare/infinity-throws-rangeerror.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: Throws if any value in a property bag for either argument is Infinity or -Infinity
+esid: sec-temporal.zoneddatetime.compare
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const other = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "gregory");
+const base = { era: "ad", month: 5, day: 2, hour: 15, timeZone: "UTC", calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.compare({ ...base, eraYear: inf }, other), `eraYear property cannot be ${inf}`);
+
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(other, { ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls1 = [];
+ const obj1 = TemporalHelpers.toPrimitiveObserver(calls1, inf, "eraYear");
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.compare({ ...base, eraYear: obj1 }, other));
+ assert.compareArray(calls1, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+
+ const calls2 = [];
+ const obj2 = TemporalHelpers.toPrimitiveObserver(calls2, inf, "eraYear");
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(other, { ...base, eraYear: obj2 }));
+ assert.compareArray(calls2, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/ZonedDateTime/from/infinity-throws-rangeerror.js b/test/intl402/Temporal/ZonedDateTime/from/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..73ced05dcb7
--- /dev/null
+++ b/test/intl402/Temporal/ZonedDateTime/from/infinity-throws-rangeerror.js
@@ -0,0 +1,22 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.zoneddatetime.from
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const base = { era: "ad", month: 5, day: 2, hour: 15, timeZone: "UTC", calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ ["constrain", "reject"].forEach((overflow) => {
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ ...base, eraYear: inf }, { overflow }), `eraYear property cannot be ${inf} (overflow ${overflow}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ ...base, eraYear: obj }, { overflow }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+ });
+});
diff --git a/test/intl402/Temporal/ZonedDateTime/prototype/equals/infinity-throws-rangeerror.js b/test/intl402/Temporal/ZonedDateTime/prototype/equals/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..e2e121c5e2b
--- /dev/null
+++ b/test/intl402/Temporal/ZonedDateTime/prototype/equals/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.zoneddatetime.prototype.equals
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "gregory");
+const base = { era: "ad", month: 5, day: 2, hour: 15, timeZone: "UTC", calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.equals({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.equals({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/ZonedDateTime/prototype/era/prop-desc.js b/test/intl402/Temporal/ZonedDateTime/prototype/era/prop-desc.js
new file mode 100644
index 00000000000..a1eefde4837
--- /dev/null
+++ b/test/intl402/Temporal/ZonedDateTime/prototype/era/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.era
+description: The "era" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "era");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/intl402/Temporal/ZonedDateTime/prototype/era/timezone-getoffsetnanosecondsfor-non-integer.js b/test/intl402/Temporal/ZonedDateTime/prototype/era/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..84c05894e6d
--- /dev/null
+++ b/test/intl402/Temporal/ZonedDateTime/prototype/era/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.era
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.era);
+});
diff --git a/test/intl402/Temporal/ZonedDateTime/prototype/era/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/intl402/Temporal/ZonedDateTime/prototype/era/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..80751c9e3cf
--- /dev/null
+++ b/test/intl402/Temporal/ZonedDateTime/prototype/era/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.era
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.era);
+});
diff --git a/test/intl402/Temporal/ZonedDateTime/prototype/era/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/intl402/Temporal/ZonedDateTime/prototype/era/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..f856f4dc995
--- /dev/null
+++ b/test/intl402/Temporal/ZonedDateTime/prototype/era/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.era
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.era);
+});
diff --git a/test/intl402/Temporal/ZonedDateTime/prototype/eraYear/calendar-returns-infinity.js b/test/intl402/Temporal/ZonedDateTime/prototype/eraYear/calendar-returns-infinity.js
new file mode 100644
index 00000000000..b8150c86721
--- /dev/null
+++ b/test/intl402/Temporal/ZonedDateTime/prototype/eraYear/calendar-returns-infinity.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.erayear
+description: Getter throws if the calendar returns ±∞ from its eraYear method
+features: [Temporal]
+---*/
+
+class InfinityCalendar extends Temporal.Calendar {
+ constructor(positive) {
+ super("iso8601");
+ this.positive = positive;
+ }
+
+ eraYear() {
+ return this.positive ? Infinity : -Infinity;
+ }
+}
+
+const pos = new InfinityCalendar(true);
+const instance1 = new Temporal.ZonedDateTime(0n, "UTC", pos);
+assert.throws(RangeError, () => instance1.eraYear);
+
+const neg = new InfinityCalendar(false);
+const instance2 = new Temporal.ZonedDateTime(0n, "UTC", neg);
+assert.throws(RangeError, () => instance2.eraYear);
diff --git a/test/intl402/Temporal/ZonedDateTime/prototype/eraYear/prop-desc.js b/test/intl402/Temporal/ZonedDateTime/prototype/eraYear/prop-desc.js
new file mode 100644
index 00000000000..b27b676a480
--- /dev/null
+++ b/test/intl402/Temporal/ZonedDateTime/prototype/eraYear/prop-desc.js
@@ -0,0 +1,14 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.erayear
+description: The "eraYear" property of Temporal.ZonedDateTime.prototype
+features: [Temporal]
+---*/
+
+const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "eraYear");
+assert.sameValue(typeof descriptor.get, "function");
+assert.sameValue(descriptor.set, undefined);
+assert.sameValue(descriptor.enumerable, false);
+assert.sameValue(descriptor.configurable, true);
diff --git a/test/intl402/Temporal/ZonedDateTime/prototype/eraYear/timezone-getoffsetnanosecondsfor-non-integer.js b/test/intl402/Temporal/ZonedDateTime/prototype/eraYear/timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 00000000000..6e7165f78ee
--- /dev/null
+++ b/test/intl402/Temporal/ZonedDateTime/prototype/eraYear/timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.erayear
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.eraYear);
+});
diff --git a/test/intl402/Temporal/ZonedDateTime/prototype/eraYear/timezone-getoffsetnanosecondsfor-out-of-range.js b/test/intl402/Temporal/ZonedDateTime/prototype/eraYear/timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 00000000000..1e61ad0532e
--- /dev/null
+++ b/test/intl402/Temporal/ZonedDateTime/prototype/eraYear/timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.erayear
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => datetime.eraYear);
+});
diff --git a/test/intl402/Temporal/ZonedDateTime/prototype/eraYear/timezone-getoffsetnanosecondsfor-wrong-type.js b/test/intl402/Temporal/ZonedDateTime/prototype/eraYear/timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 00000000000..84c119a595e
--- /dev/null
+++ b/test/intl402/Temporal/ZonedDateTime/prototype/eraYear/timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-get-temporal.zoneddatetime.prototype.erayear
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => datetime.eraYear);
+});
diff --git a/test/intl402/Temporal/ZonedDateTime/prototype/since/infinity-throws-rangeerror.js b/test/intl402/Temporal/ZonedDateTime/prototype/since/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..26e0b483e9f
--- /dev/null
+++ b/test/intl402/Temporal/ZonedDateTime/prototype/since/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.zoneddatetime.prototype.since
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "gregory");
+const base = { era: "ad", month: 5, day: 2, hour: 15, timeZone: "UTC", calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.since({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.since({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/ZonedDateTime/prototype/until/infinity-throws-rangeerror.js b/test/intl402/Temporal/ZonedDateTime/prototype/until/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..0e4c9b45ab0
--- /dev/null
+++ b/test/intl402/Temporal/ZonedDateTime/prototype/until/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.zoneddatetime.prototype.until
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "gregory");
+const base = { era: "ad", month: 5, day: 2, hour: 15, timeZone: "UTC", calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.until({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.until({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});
diff --git a/test/intl402/Temporal/ZonedDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js b/test/intl402/Temporal/ZonedDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js
new file mode 100644
index 00000000000..8e305dfe850
--- /dev/null
+++ b/test/intl402/Temporal/ZonedDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js
@@ -0,0 +1,21 @@
+// 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 eraYear in the property bag is Infinity or -Infinity
+esid: sec-temporal.zoneddatetime.prototype.withplaindate
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "gregory");
+const base = { era: "ad", month: 5, day: 2, calendar: "gregory" };
+
+[Infinity, -Infinity].forEach((inf) => {
+ assert.throws(RangeError, () => instance.withPlainDate({ ...base, eraYear: inf }), `eraYear property cannot be ${inf}`);
+
+ const calls = [];
+ const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, "eraYear");
+ assert.throws(RangeError, () => instance.withPlainDate({ ...base, eraYear: obj }));
+ assert.compareArray(calls, ["get eraYear.valueOf", "call eraYear.valueOf"], "it fails after fetching the primitive value");
+});