1 // GENERATED, DO NOT EDIT
2 // file: asyncHelpers.js
3 // Copyright (C) 2022 Igalia, S.L. All rights reserved.
4 // This code is governed by the BSD license found in the LICENSE file.
7 A collection of assertion and wrapper functions for testing asynchronous built-ins.
11 function asyncTest(testFunc) {
12 if (!Object.hasOwn(globalThis, "$DONE")) {
13 throw new Test262Error("asyncTest called without async flag");
15 if (typeof testFunc !== "function") {
16 $DONE(new Test262Error("asyncTest called with non-function argument"));
33 assert.throwsAsync = async function (expectedErrorConstructor, func, message) {
35 if (message === undefined) {
40 if (typeof func === "function") {
42 innerThenable = func();
44 innerThenable === null ||
45 typeof innerThenable !== "object" ||
46 typeof innerThenable.then !== "function"
49 "Expected to obtain an inner promise that would reject with a" +
50 expectedErrorConstructor.name +
51 " but result was not a thenable";
52 throw new Test262Error(message);
57 expectedErrorConstructor.name +
58 " to be thrown asynchronously but an exception was thrown synchronously while obtaining the inner promise";
59 throw new Test262Error(message);
63 "assert.throwsAsync called with an argument that is not a function";
64 throw new Test262Error(message);
68 return innerThenable.then(
72 expectedErrorConstructor.name +
73 " to be thrown asynchronously but no exception was thrown at all";
74 throw new Test262Error(message);
77 var expectedName, actualName;
78 if (typeof thrown !== "object" || thrown === null) {
79 message += "Thrown value was not an object!";
80 throw new Test262Error(message);
81 } else if (thrown.constructor !== expectedErrorConstructor) {
82 expectedName = expectedErrorConstructor.name;
83 actualName = thrown.constructor.name;
84 if (expectedName === actualName) {
88 " but got a different error constructor with the same name";
91 "Expected a " + expectedName + " but got a " + actualName;
93 throw new Test262Error(message);
98 if (typeof thrown !== "object" || thrown === null) {
101 expectedErrorConstructor.name +
102 " to be thrown asynchronously but innerThenable synchronously threw a value that was not an object ";
106 expectedErrorConstructor.name +
107 " to be thrown asynchronously but a " +
108 thrown.constructor.name +
109 " was thrown synchronously";
111 throw new Test262Error(message);
115 // file: temporalHelpers.js
116 // Copyright (C) 2021 Igalia, S.L. All rights reserved.
117 // This code is governed by the BSD license found in the LICENSE file.
120 This defines helper objects and functions for testing Temporal.
121 defines: [TemporalHelpers]
122 features: [Symbol.species, Symbol.iterator, Temporal]
125 const ASCII_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/u;
127 function formatPropertyName(propertyKey, objectName = "") {
128 switch (typeof propertyKey) {
130 if (Symbol.keyFor(propertyKey) !== undefined) {
131 return `${objectName}[Symbol.for('${Symbol.keyFor(propertyKey)}')]`;
132 } else if (propertyKey.description.startsWith('Symbol.')) {
133 return `${objectName}[${propertyKey.description}]`;
135 return `${objectName}[Symbol('${propertyKey.description}')]`
138 if (propertyKey !== String(Number(propertyKey))) {
139 if (ASCII_IDENTIFIER.test(propertyKey)) {
140 return objectName ? `${objectName}.${propertyKey}` : propertyKey;
142 return `${objectName}['${propertyKey.replace(/'/g, "\\'")}']`
146 // integer or string integer-index
147 return `${objectName}[${propertyKey}]`;
151 const SKIP_SYMBOL = Symbol("Skip");
153 var TemporalHelpers = {
155 * Codes and maximum lengths of months in the ISO 8601 calendar.
158 { month: 1, monthCode: "M01", daysInMonth: 31 },
159 { month: 2, monthCode: "M02", daysInMonth: 29 },
160 { month: 3, monthCode: "M03", daysInMonth: 31 },
161 { month: 4, monthCode: "M04", daysInMonth: 30 },
162 { month: 5, monthCode: "M05", daysInMonth: 31 },
163 { month: 6, monthCode: "M06", daysInMonth: 30 },
164 { month: 7, monthCode: "M07", daysInMonth: 31 },
165 { month: 8, monthCode: "M08", daysInMonth: 31 },
166 { month: 9, monthCode: "M09", daysInMonth: 30 },
167 { month: 10, monthCode: "M10", daysInMonth: 31 },
168 { month: 11, monthCode: "M11", daysInMonth: 30 },
169 { month: 12, monthCode: "M12", daysInMonth: 31 }
173 * assertDuration(duration, years, ..., nanoseconds[, description]):
175 * Shorthand for asserting that each field of a Temporal.Duration is equal to
178 assertDuration(duration, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, description = "") {
179 const prefix = description ? `${description}: ` : "";
180 assert(duration instanceof Temporal.Duration, `${prefix}instanceof`);
181 assert.sameValue(duration.years, years, `${prefix}years result:`);
182 assert.sameValue(duration.months, months, `${prefix}months result:`);
183 assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`);
184 assert.sameValue(duration.days, days, `${prefix}days result:`);
185 assert.sameValue(duration.hours, hours, `${prefix}hours result:`);
186 assert.sameValue(duration.minutes, minutes, `${prefix}minutes result:`);
187 assert.sameValue(duration.seconds, seconds, `${prefix}seconds result:`);
188 assert.sameValue(duration.milliseconds, milliseconds, `${prefix}milliseconds result:`);
189 assert.sameValue(duration.microseconds, microseconds, `${prefix}microseconds result:`);
190 assert.sameValue(duration.nanoseconds, nanoseconds, `${prefix}nanoseconds result`);
194 * assertDateDuration(duration, years, months, weeks, days, [, description]):
196 * Shorthand for asserting that each date field of a Temporal.Duration is
197 * equal to an expected value.
199 assertDateDuration(duration, years, months, weeks, days, description = "") {
200 const prefix = description ? `${description}: ` : "";
201 assert(duration instanceof Temporal.Duration, `${prefix}instanceof`);
202 assert.sameValue(duration.years, years, `${prefix}years result:`);
203 assert.sameValue(duration.months, months, `${prefix}months result:`);
204 assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`);
205 assert.sameValue(duration.days, days, `${prefix}days result:`);
206 assert.sameValue(duration.hours, 0, `${prefix}hours result should be zero:`);
207 assert.sameValue(duration.minutes, 0, `${prefix}minutes result should be zero:`);
208 assert.sameValue(duration.seconds, 0, `${prefix}seconds result should be zero:`);
209 assert.sameValue(duration.milliseconds, 0, `${prefix}milliseconds result should be zero:`);
210 assert.sameValue(duration.microseconds, 0, `${prefix}microseconds result should be zero:`);
211 assert.sameValue(duration.nanoseconds, 0, `${prefix}nanoseconds result should be zero:`);
215 * assertDurationsEqual(actual, expected[, description]):
217 * Shorthand for asserting that each field of a Temporal.Duration is equal to
218 * the corresponding field in another Temporal.Duration.
220 assertDurationsEqual(actual, expected, description = "") {
221 const prefix = description ? `${description}: ` : "";
222 assert(expected instanceof Temporal.Duration, `${prefix}expected value should be a Temporal.Duration`);
223 TemporalHelpers.assertDuration(actual, expected.years, expected.months, expected.weeks, expected.days, expected.hours, expected.minutes, expected.seconds, expected.milliseconds, expected.microseconds, expected.nanoseconds, description);
227 * assertInstantsEqual(actual, expected[, description]):
229 * Shorthand for asserting that two Temporal.Instants are of the correct type
230 * and equal according to their equals() methods.
232 assertInstantsEqual(actual, expected, description = "") {
233 const prefix = description ? `${description}: ` : "";
234 assert(expected instanceof Temporal.Instant, `${prefix}expected value should be a Temporal.Instant`);
235 assert(actual instanceof Temporal.Instant, `${prefix}instanceof`);
236 assert(actual.equals(expected), `${prefix}equals method`);
240 * assertPlainDate(date, year, ..., nanosecond[, description[, era, eraYear]]):
242 * Shorthand for asserting that each field of a Temporal.PlainDate is equal to
243 * an expected value. (Except the `calendar` property, since callers may want
244 * to assert either object equality with an object they put in there, or the
245 * value of date.calendarId.)
247 assertPlainDate(date, year, month, monthCode, day, description = "", era = undefined, eraYear = undefined) {
248 const prefix = description ? `${description}: ` : "";
249 assert(date instanceof Temporal.PlainDate, `${prefix}instanceof`);
250 assert.sameValue(date.era, era, `${prefix}era result:`);
251 assert.sameValue(date.eraYear, eraYear, `${prefix}eraYear result:`);
252 assert.sameValue(date.year, year, `${prefix}year result:`);
253 assert.sameValue(date.month, month, `${prefix}month result:`);
254 assert.sameValue(date.monthCode, monthCode, `${prefix}monthCode result:`);
255 assert.sameValue(date.day, day, `${prefix}day result:`);
259 * assertPlainDateTime(datetime, year, ..., nanosecond[, description[, era, eraYear]]):
261 * Shorthand for asserting that each field of a Temporal.PlainDateTime is
262 * equal to an expected value. (Except the `calendar` property, since callers
263 * may want to assert either object equality with an object they put in there,
264 * or the value of datetime.calendarId.)
266 assertPlainDateTime(datetime, year, month, monthCode, day, hour, minute, second, millisecond, microsecond, nanosecond, description = "", era = undefined, eraYear = undefined) {
267 const prefix = description ? `${description}: ` : "";
268 assert(datetime instanceof Temporal.PlainDateTime, `${prefix}instanceof`);
269 assert.sameValue(datetime.era, era, `${prefix}era result:`);
270 assert.sameValue(datetime.eraYear, eraYear, `${prefix}eraYear result:`);
271 assert.sameValue(datetime.year, year, `${prefix}year result:`);
272 assert.sameValue(datetime.month, month, `${prefix}month result:`);
273 assert.sameValue(datetime.monthCode, monthCode, `${prefix}monthCode result:`);
274 assert.sameValue(datetime.day, day, `${prefix}day result:`);
275 assert.sameValue(datetime.hour, hour, `${prefix}hour result:`);
276 assert.sameValue(datetime.minute, minute, `${prefix}minute result:`);
277 assert.sameValue(datetime.second, second, `${prefix}second result:`);
278 assert.sameValue(datetime.millisecond, millisecond, `${prefix}millisecond result:`);
279 assert.sameValue(datetime.microsecond, microsecond, `${prefix}microsecond result:`);
280 assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`);
284 * assertPlainDateTimesEqual(actual, expected[, description]):
286 * Shorthand for asserting that two Temporal.PlainDateTimes are of the correct
287 * type, equal according to their equals() methods, and additionally that
288 * their calendar internal slots are the same value.
290 assertPlainDateTimesEqual(actual, expected, description = "") {
291 const prefix = description ? `${description}: ` : "";
292 assert(expected instanceof Temporal.PlainDateTime, `${prefix}expected value should be a Temporal.PlainDateTime`);
293 assert(actual instanceof Temporal.PlainDateTime, `${prefix}instanceof`);
294 assert(actual.equals(expected), `${prefix}equals method`);
296 actual.getISOFields().calendar,
297 expected.getISOFields().calendar,
298 `${prefix}calendar same value:`
303 * assertPlainMonthDay(monthDay, monthCode, day[, description [, referenceISOYear]]):
305 * Shorthand for asserting that each field of a Temporal.PlainMonthDay is
306 * equal to an expected value. (Except the `calendar` property, since callers
307 * may want to assert either object equality with an object they put in there,
308 * or the value of monthDay.calendarId().)
310 assertPlainMonthDay(monthDay, monthCode, day, description = "", referenceISOYear = 1972) {
311 const prefix = description ? `${description}: ` : "";
312 assert(monthDay instanceof Temporal.PlainMonthDay, `${prefix}instanceof`);
313 assert.sameValue(monthDay.monthCode, monthCode, `${prefix}monthCode result:`);
314 assert.sameValue(monthDay.day, day, `${prefix}day result:`);
315 assert.sameValue(monthDay.getISOFields().isoYear, referenceISOYear, `${prefix}referenceISOYear result:`);
319 * assertPlainTime(time, hour, ..., nanosecond[, description]):
321 * Shorthand for asserting that each field of a Temporal.PlainTime is equal to
324 assertPlainTime(time, hour, minute, second, millisecond, microsecond, nanosecond, description = "") {
325 const prefix = description ? `${description}: ` : "";
326 assert(time instanceof Temporal.PlainTime, `${prefix}instanceof`);
327 assert.sameValue(time.hour, hour, `${prefix}hour result:`);
328 assert.sameValue(time.minute, minute, `${prefix}minute result:`);
329 assert.sameValue(time.second, second, `${prefix}second result:`);
330 assert.sameValue(time.millisecond, millisecond, `${prefix}millisecond result:`);
331 assert.sameValue(time.microsecond, microsecond, `${prefix}microsecond result:`);
332 assert.sameValue(time.nanosecond, nanosecond, `${prefix}nanosecond result:`);
336 * assertPlainTimesEqual(actual, expected[, description]):
338 * Shorthand for asserting that two Temporal.PlainTimes are of the correct
339 * type and equal according to their equals() methods.
341 assertPlainTimesEqual(actual, expected, description = "") {
342 const prefix = description ? `${description}: ` : "";
343 assert(expected instanceof Temporal.PlainTime, `${prefix}expected value should be a Temporal.PlainTime`);
344 assert(actual instanceof Temporal.PlainTime, `${prefix}instanceof`);
345 assert(actual.equals(expected), `${prefix}equals method`);
349 * assertPlainYearMonth(yearMonth, year, month, monthCode[, description[, era, eraYear, referenceISODay]]):
351 * Shorthand for asserting that each field of a Temporal.PlainYearMonth is
352 * equal to an expected value. (Except the `calendar` property, since callers
353 * may want to assert either object equality with an object they put in there,
354 * or the value of yearMonth.calendarId.)
356 assertPlainYearMonth(yearMonth, year, month, monthCode, description = "", era = undefined, eraYear = undefined, referenceISODay = 1) {
357 const prefix = description ? `${description}: ` : "";
358 assert(yearMonth instanceof Temporal.PlainYearMonth, `${prefix}instanceof`);
359 assert.sameValue(yearMonth.era, era, `${prefix}era result:`);
360 assert.sameValue(yearMonth.eraYear, eraYear, `${prefix}eraYear result:`);
361 assert.sameValue(yearMonth.year, year, `${prefix}year result:`);
362 assert.sameValue(yearMonth.month, month, `${prefix}month result:`);
363 assert.sameValue(yearMonth.monthCode, monthCode, `${prefix}monthCode result:`);
364 assert.sameValue(yearMonth.getISOFields().isoDay, referenceISODay, `${prefix}referenceISODay result:`);
368 * assertZonedDateTimesEqual(actual, expected[, description]):
370 * Shorthand for asserting that two Temporal.ZonedDateTimes are of the correct
371 * type, equal according to their equals() methods, and additionally that
372 * their time zones and calendar internal slots are the same value.
374 assertZonedDateTimesEqual(actual, expected, description = "") {
375 const prefix = description ? `${description}: ` : "";
376 assert(expected instanceof Temporal.ZonedDateTime, `${prefix}expected value should be a Temporal.ZonedDateTime`);
377 assert(actual instanceof Temporal.ZonedDateTime, `${prefix}instanceof`);
378 assert(actual.equals(expected), `${prefix}equals method`);
379 assert.sameValue(actual.timeZone, expected.timeZone, `${prefix}time zone same value:`);
381 actual.getISOFields().calendar,
382 expected.getISOFields().calendar,
383 `${prefix}calendar same value:`
388 * assertUnreachable(description):
390 * Helper for asserting that code is not executed. This is useful for
391 * assertions that methods of user calendars and time zones are not called.
393 assertUnreachable(description) {
394 let message = "This code should not be executed";
396 message = `${message}: ${description}`;
398 throw new Test262Error(message);
402 * checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls):
404 * When an options object with a largestUnit property is synthesized inside
405 * Temporal and passed to user code such as calendar.dateUntil(), the value of
406 * the largestUnit property should be in the singular form, even if the input
407 * was given in the plural form.
408 * (This doesn't apply when the options object is passed through verbatim.)
410 * func(calendar, largestUnit, index) is the operation under test. It's called
411 * with an instance of a calendar that keeps track of which largestUnit is
412 * passed to dateUntil(), each key of expectedLargestUnitCalls in turn, and
413 * the key's numerical index in case the function needs to generate test data
414 * based on the index. At the end, the actual values passed to dateUntil() are
415 * compared with the array values of expectedLargestUnitCalls.
417 checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls) {
420 class DateUntilOptionsCalendar extends Temporal.Calendar {
425 dateUntil(earlier, later, options) {
426 actual.push(options.largestUnit);
427 return super.dateUntil(earlier, later, options);
431 return "date-until-options";
435 const calendar = new DateUntilOptionsCalendar();
436 Object.entries(expectedLargestUnitCalls).forEach(([largestUnit, expected], index) => {
437 func(calendar, largestUnit, index);
438 assert.compareArray(actual, expected, `largestUnit passed to calendar.dateUntil() for largestUnit ${largestUnit}`);
439 actual.splice(0); // empty it for the next check
444 * checkPlainDateTimeConversionFastPath(func):
446 * ToTemporalDate and ToTemporalTime should both, if given a
447 * Temporal.PlainDateTime instance, convert to the desired type by reading the
448 * PlainDateTime's internal slots, rather than calling any getters.
450 * func(datetime, calendar) is the actual operation to test, that must
451 * internally call the abstract operation ToTemporalDate or ToTemporalTime.
452 * It is passed a Temporal.PlainDateTime instance, as well as the instance's
453 * calendar object (so that it doesn't have to call the calendar getter itself
454 * if it wants to make any assertions about the calendar.)
456 checkPlainDateTimeConversionFastPath(func, message = "checkPlainDateTimeConversionFastPath") {
460 const calendar = new Temporal.Calendar("iso8601");
461 const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
462 const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDateTime.prototype);
463 ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => {
464 Object.defineProperty(datetime, property, {
466 actual.push(`get ${formatPropertyName(property)}`);
467 const value = prototypeDescrs[property].get.call(this);
470 actual.push(`toString ${formatPropertyName(property)}`);
471 return value.toString();
474 actual.push(`valueOf ${formatPropertyName(property)}`);
481 Object.defineProperty(datetime, "calendar", {
483 actual.push("get calendar");
488 func(datetime, calendar);
489 assert.compareArray(actual, expected, `${message}: property getters not called`);
493 * Check that an options bag that accepts units written in the singular form,
494 * also accepts the same units written in the plural form.
495 * func(unit) should call the method with the appropriate options bag
496 * containing unit as a value. This will be called twice for each element of
497 * validSingularUnits, once with singular and once with plural, and the
498 * results of each pair should be the same (whether a Temporal object or a
501 checkPluralUnitsAccepted(func, validSingularUnits) {
510 millisecond: 'milliseconds',
511 microsecond: 'microseconds',
512 nanosecond: 'nanoseconds',
515 validSingularUnits.forEach((unit) => {
516 const singularValue = func(unit);
517 const pluralValue = func(plurals[unit]);
518 const desc = `Plural ${plurals[unit]} produces the same result as singular ${unit}`;
519 if (singularValue instanceof Temporal.Duration) {
520 TemporalHelpers.assertDurationsEqual(pluralValue, singularValue, desc);
521 } else if (singularValue instanceof Temporal.Instant) {
522 TemporalHelpers.assertInstantsEqual(pluralValue, singularValue, desc);
523 } else if (singularValue instanceof Temporal.PlainDateTime) {
524 TemporalHelpers.assertPlainDateTimesEqual(pluralValue, singularValue, desc);
525 } else if (singularValue instanceof Temporal.PlainTime) {
526 TemporalHelpers.assertPlainTimesEqual(pluralValue, singularValue, desc);
527 } else if (singularValue instanceof Temporal.ZonedDateTime) {
528 TemporalHelpers.assertZonedDateTimesEqual(pluralValue, singularValue, desc);
530 assert.sameValue(pluralValue, singularValue);
536 * checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc):
538 * Checks the type handling of the roundingIncrement option.
539 * checkFunc(roundingIncrement) is a function which takes the value of
540 * roundingIncrement to test, and calls the method under test with it,
541 * returning the result. assertTrueResultFunc(result, description) should
542 * assert that result is the expected result with roundingIncrement: true, and
543 * assertObjectResultFunc(result, description) should assert that result is
544 * the expected result with roundingIncrement being an object with a valueOf()
547 checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc) {
548 // null converts to 0, which is out of range
549 assert.throws(RangeError, () => checkFunc(null), "null");
550 // Booleans convert to either 0 or 1, and 1 is allowed
551 const trueResult = checkFunc(true);
552 assertTrueResultFunc(trueResult, "true");
553 assert.throws(RangeError, () => checkFunc(false), "false");
554 // Symbols and BigInts cannot convert to numbers
555 assert.throws(TypeError, () => checkFunc(Symbol()), "symbol");
556 assert.throws(TypeError, () => checkFunc(2n), "bigint");
558 // Objects prefer their valueOf() methods when converting to a number
559 assert.throws(RangeError, () => checkFunc({}), "plain object");
562 "get roundingIncrement.valueOf",
563 "call roundingIncrement.valueOf",
566 const observer = TemporalHelpers.toPrimitiveObserver(actual, 2, "roundingIncrement");
567 const objectResult = checkFunc(observer);
568 assertObjectResultFunc(objectResult, "object with valueOf");
569 assert.compareArray(actual, expected, "order of operations");
573 * checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc):
575 * Checks the type handling of a string option, of which there are several in
577 * propertyName is the name of the option, and value is the value that
578 * assertFunc should expect it to have.
579 * checkFunc(value) is a function which takes the value of the option to test,
580 * and calls the method under test with it, returning the result.
581 * assertFunc(result, description) should assert that result is the expected
582 * result with the option value being an object with a toString() method
583 * which returns the given value.
585 checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc) {
586 // null converts to the string "null", which is an invalid string value
587 assert.throws(RangeError, () => checkFunc(null), "null");
588 // Booleans convert to the strings "true" or "false", which are invalid
589 assert.throws(RangeError, () => checkFunc(true), "true");
590 assert.throws(RangeError, () => checkFunc(false), "false");
591 // Symbols cannot convert to strings
592 assert.throws(TypeError, () => checkFunc(Symbol()), "symbol");
593 // Numbers convert to strings which are invalid
594 assert.throws(RangeError, () => checkFunc(2), "number");
595 // BigInts convert to strings which are invalid
596 assert.throws(RangeError, () => checkFunc(2n), "bigint");
598 // Objects prefer their toString() methods when converting to a string
599 assert.throws(RangeError, () => checkFunc({}), "plain object");
602 `get ${propertyName}.toString`,
603 `call ${propertyName}.toString`,
606 const observer = TemporalHelpers.toPrimitiveObserver(actual, value, propertyName);
607 const result = checkFunc(observer);
608 assertFunc(result, "object with toString");
609 assert.compareArray(actual, expected, "order of operations");
613 * checkSubclassingIgnored(construct, constructArgs, method, methodArgs,
616 * Methods of Temporal classes that return a new instance of the same class,
617 * must not take the constructor of a subclass into account, nor the @@species
618 * property. This helper runs tests to ensure this.
620 * construct(...constructArgs) must yield a valid instance of the Temporal
621 * class. instance[method](...methodArgs) is the method call under test, which
622 * must also yield a valid instance of the same Temporal class, not a
623 * subclass. See below for the individual tests that this runs.
624 * resultAssertions() is a function that performs additional assertions on the
625 * instance returned by the method under test.
627 checkSubclassingIgnored(...args) {
628 this.checkSubclassConstructorNotObject(...args);
629 this.checkSubclassConstructorUndefined(...args);
630 this.checkSubclassConstructorThrows(...args);
631 this.checkSubclassConstructorNotCalled(...args);
632 this.checkSubclassSpeciesInvalidResult(...args);
633 this.checkSubclassSpeciesNotAConstructor(...args);
634 this.checkSubclassSpeciesNull(...args);
635 this.checkSubclassSpeciesUndefined(...args);
636 this.checkSubclassSpeciesThrows(...args);
640 * Checks that replacing the 'constructor' property of the instance with
641 * various primitive values does not affect the returned new instance.
643 checkSubclassConstructorNotObject(construct, constructArgs, method, methodArgs, resultAssertions) {
644 function check(value, description) {
645 const instance = new construct(...constructArgs);
646 instance.constructor = value;
647 const result = instance[method](...methodArgs);
648 assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
649 resultAssertions(result);
654 check("test", "string");
655 check(Symbol(), "Symbol");
661 * Checks that replacing the 'constructor' property of the subclass with
662 * undefined does not affect the returned new instance.
664 checkSubclassConstructorUndefined(construct, constructArgs, method, methodArgs, resultAssertions) {
667 class MySubclass extends construct {
670 super(...constructArgs);
674 const instance = new MySubclass();
675 assert.sameValue(called, 1);
677 MySubclass.prototype.constructor = undefined;
679 const result = instance[method](...methodArgs);
680 assert.sameValue(called, 1);
681 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
682 resultAssertions(result);
686 * Checks that making the 'constructor' property of the instance throw when
687 * called does not affect the returned new instance.
689 checkSubclassConstructorThrows(construct, constructArgs, method, methodArgs, resultAssertions) {
690 function CustomError() {}
691 const instance = new construct(...constructArgs);
692 Object.defineProperty(instance, "constructor", {
694 throw new CustomError();
697 const result = instance[method](...methodArgs);
698 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
699 resultAssertions(result);
703 * Checks that when subclassing, the subclass constructor is not called by
704 * the method under test.
706 checkSubclassConstructorNotCalled(construct, constructArgs, method, methodArgs, resultAssertions) {
709 class MySubclass extends construct {
712 super(...constructArgs);
716 const instance = new MySubclass();
717 assert.sameValue(called, 1);
719 const result = instance[method](...methodArgs);
720 assert.sameValue(called, 1);
721 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
722 resultAssertions(result);
726 * Check that the constructor's @@species property is ignored when it's a
727 * constructor that returns a non-object value.
729 checkSubclassSpeciesInvalidResult(construct, constructArgs, method, methodArgs, resultAssertions) {
730 function check(value, description) {
731 const instance = new construct(...constructArgs);
732 instance.constructor = {
733 [Symbol.species]: function() {
737 const result = instance[method](...methodArgs);
738 assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
739 resultAssertions(result);
742 check(undefined, "undefined");
745 check("test", "string");
746 check(Symbol(), "Symbol");
749 check({}, "plain object");
753 * Check that the constructor's @@species property is ignored when it's not a
756 checkSubclassSpeciesNotAConstructor(construct, constructArgs, method, methodArgs, resultAssertions) {
757 function check(value, description) {
758 const instance = new construct(...constructArgs);
759 instance.constructor = {
760 [Symbol.species]: value,
762 const result = instance[method](...methodArgs);
763 assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
764 resultAssertions(result);
768 check("test", "string");
769 check(Symbol(), "Symbol");
772 check({}, "plain object");
776 * Check that the constructor's @@species property is ignored when it's null.
778 checkSubclassSpeciesNull(construct, constructArgs, method, methodArgs, resultAssertions) {
781 class MySubclass extends construct {
784 super(...constructArgs);
788 const instance = new MySubclass();
789 assert.sameValue(called, 1);
791 MySubclass.prototype.constructor = {
792 [Symbol.species]: null,
795 const result = instance[method](...methodArgs);
796 assert.sameValue(called, 1);
797 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
798 resultAssertions(result);
802 * Check that the constructor's @@species property is ignored when it's
805 checkSubclassSpeciesUndefined(construct, constructArgs, method, methodArgs, resultAssertions) {
808 class MySubclass extends construct {
811 super(...constructArgs);
815 const instance = new MySubclass();
816 assert.sameValue(called, 1);
818 MySubclass.prototype.constructor = {
819 [Symbol.species]: undefined,
822 const result = instance[method](...methodArgs);
823 assert.sameValue(called, 1);
824 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
825 resultAssertions(result);
829 * Check that the constructor's @@species property is ignored when it throws,
830 * i.e. it is not called at all.
832 checkSubclassSpeciesThrows(construct, constructArgs, method, methodArgs, resultAssertions) {
833 function CustomError() {}
835 const instance = new construct(...constructArgs);
836 instance.constructor = {
837 get [Symbol.species]() {
838 throw new CustomError();
842 const result = instance[method](...methodArgs);
843 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
847 * checkSubclassingIgnoredStatic(construct, method, methodArgs, resultAssertions):
849 * Static methods of Temporal classes that return a new instance of the class,
850 * must not use the this-value as a constructor. This helper runs tests to
853 * construct[method](...methodArgs) is the static method call under test, and
854 * must yield a valid instance of the Temporal class, not a subclass. See
855 * below for the individual tests that this runs.
856 * resultAssertions() is a function that performs additional assertions on the
857 * instance returned by the method under test.
859 checkSubclassingIgnoredStatic(...args) {
860 this.checkStaticInvalidReceiver(...args);
861 this.checkStaticReceiverNotCalled(...args);
862 this.checkThisValueNotCalled(...args);
866 * Check that calling the static method with a receiver that's not callable,
867 * still calls the intrinsic constructor.
869 checkStaticInvalidReceiver(construct, method, methodArgs, resultAssertions) {
870 function check(value, description) {
871 const result = construct[method].apply(value, methodArgs);
872 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
873 resultAssertions(result);
876 check(undefined, "undefined");
879 check("test", "string");
880 check(Symbol(), "symbol");
883 check({}, "Non-callable object");
887 * Check that calling the static method with a receiver that returns a value
888 * that's not callable, still calls the intrinsic constructor.
890 checkStaticReceiverNotCalled(construct, method, methodArgs, resultAssertions) {
891 function check(value, description) {
892 const receiver = function () {
895 const result = construct[method].apply(receiver, methodArgs);
896 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
897 resultAssertions(result);
900 check(undefined, "undefined");
903 check("test", "string");
904 check(Symbol(), "symbol");
907 check({}, "Non-callable object");
911 * Check that the receiver isn't called.
913 checkThisValueNotCalled(construct, method, methodArgs, resultAssertions) {
916 class MySubclass extends construct {
917 constructor(...args) {
923 const result = MySubclass[method](...methodArgs);
924 assert.sameValue(called, false);
925 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
926 resultAssertions(result);
930 * Check that any iterable returned from a custom time zone's
931 * getPossibleInstantsFor() method is exhausted.
932 * The custom time zone object is passed in to func().
933 * expected is an array of strings representing the expected calls to the
934 * getPossibleInstantsFor() method. The PlainDateTimes that it is called with,
935 * are compared (using their toString() results) with the array.
937 checkTimeZonePossibleInstantsIterable(func, expected) {
938 // A custom time zone that returns an iterable instead of an array from its
939 // getPossibleInstantsFor() method, and for testing purposes skips
940 // 00:00-01:00 UTC on January 1, 2030, and repeats 00:00-01:00 UTC+1 on
941 // January 3, 2030. Otherwise identical to the UTC time zone.
942 class TimeZonePossibleInstantsIterable extends Temporal.TimeZone {
945 this.getPossibleInstantsForCallCount = 0;
946 this.getPossibleInstantsForCalledWith = [];
947 this.getPossibleInstantsForReturns = [];
948 this.iteratorExhausted = [];
952 return "Custom/Iterable";
955 getOffsetNanosecondsFor(instant) {
956 if (Temporal.Instant.compare(instant, "2030-01-01T00:00Z") >= 0 &&
957 Temporal.Instant.compare(instant, "2030-01-03T01:00Z") < 0) {
958 return 3600_000_000_000;
964 getPossibleInstantsFor(dateTime) {
965 this.getPossibleInstantsForCallCount++;
966 this.getPossibleInstantsForCalledWith.push(dateTime);
968 // Fake DST transition
969 let retval = super.getPossibleInstantsFor(dateTime);
970 if (dateTime.toPlainDate().equals("2030-01-01") && dateTime.hour === 0) {
972 } else if (dateTime.toPlainDate().equals("2030-01-03") && dateTime.hour === 0) {
973 retval.push(retval[0].subtract({ hours: 1 }));
974 } else if (dateTime.year === 2030 && dateTime.month === 1 && dateTime.day >= 1 && dateTime.day <= 2) {
975 retval[0] = retval[0].subtract({ hours: 1 });
978 this.getPossibleInstantsForReturns.push(retval);
979 this.iteratorExhausted.push(false);
981 callIndex: this.getPossibleInstantsForCallCount - 1,
983 *[Symbol.iterator]() {
984 yield* this.timeZone.getPossibleInstantsForReturns[this.callIndex];
985 this.timeZone.iteratorExhausted[this.callIndex] = true;
991 const timeZone = new TimeZonePossibleInstantsIterable();
994 assert.sameValue(timeZone.getPossibleInstantsForCallCount, expected.length, "getPossibleInstantsFor() method called correct number of times");
996 for (let index = 0; index < expected.length; index++) {
997 assert.sameValue(timeZone.getPossibleInstantsForCalledWith[index].toString(), expected[index], "getPossibleInstantsFor() called with expected PlainDateTime");
998 assert(timeZone.iteratorExhausted[index], "iterated through the whole iterable");
1003 * Check that any calendar-carrying Temporal object has its [[Calendar]]
1004 * internal slot read by ToTemporalCalendar, and does not fetch the calendar
1005 * by calling getters.
1006 * The custom calendar object is passed in to func() so that it can do its
1007 * own additional assertions involving the calendar if necessary. (Sometimes
1008 * there is nothing to assert as the calendar isn't stored anywhere that can
1009 * be asserted about.)
1011 checkToTemporalCalendarFastPath(func) {
1012 class CalendarFastPathCheck extends Temporal.Calendar {
1017 dateFromFields(...args) {
1018 return super.dateFromFields(...args).withCalendar(this);
1021 monthDayFromFields(...args) {
1022 const { isoYear, isoMonth, isoDay } = super.monthDayFromFields(...args).getISOFields();
1023 return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
1026 yearMonthFromFields(...args) {
1027 const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields(...args).getISOFields();
1028 return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
1032 return "fast-path-check";
1035 const calendar = new CalendarFastPathCheck();
1037 const plainDate = new Temporal.PlainDate(2000, 5, 2, calendar);
1038 const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
1039 const plainMonthDay = new Temporal.PlainMonthDay(5, 2, calendar);
1040 const plainYearMonth = new Temporal.PlainYearMonth(2000, 5, calendar);
1041 const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
1043 [plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((temporalObject) => {
1045 const expected = [];
1047 Object.defineProperty(temporalObject, "calendar", {
1049 actual.push("get calendar");
1054 func(temporalObject, calendar);
1055 assert.compareArray(actual, expected, "calendar getter not called");
1059 checkToTemporalInstantFastPath(func) {
1061 const expected = [];
1063 const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
1064 Object.defineProperty(datetime, 'toString', {
1066 actual.push("get toString");
1067 return function (options) {
1068 actual.push("call toString");
1069 return Temporal.ZonedDateTime.prototype.toString.call(this, options);
1075 assert.compareArray(actual, expected, "toString not called");
1078 checkToTemporalPlainDateTimeFastPath(func) {
1080 const expected = [];
1082 const calendar = new Temporal.Calendar("iso8601");
1083 const date = new Temporal.PlainDate(2000, 5, 2, calendar);
1084 const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDate.prototype);
1085 ["year", "month", "monthCode", "day"].forEach((property) => {
1086 Object.defineProperty(date, property, {
1088 actual.push(`get ${formatPropertyName(property)}`);
1089 const value = prototypeDescrs[property].get.call(this);
1090 return TemporalHelpers.toPrimitiveObserver(actual, value, property);
1094 ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => {
1095 Object.defineProperty(date, property, {
1097 actual.push(`get ${formatPropertyName(property)}`);
1102 Object.defineProperty(date, "calendar", {
1104 actual.push("get calendar");
1109 func(date, calendar);
1110 assert.compareArray(actual, expected, "property getters not called");
1114 * A custom calendar used in prototype pollution checks. Verifies that the
1115 * fromFields methods are always called with a null-prototype fields object.
1117 calendarCheckFieldsPrototypePollution() {
1118 class CalendarCheckFieldsPrototypePollution extends Temporal.Calendar {
1121 this.dateFromFieldsCallCount = 0;
1122 this.yearMonthFromFieldsCallCount = 0;
1123 this.monthDayFromFieldsCallCount = 0;
1126 // toString must remain "iso8601", so that some methods don't throw due to
1127 // incompatible calendars
1129 dateFromFields(fields, options = {}) {
1130 this.dateFromFieldsCallCount++;
1131 assert.sameValue(Object.getPrototypeOf(fields), null, "dateFromFields should be called with null-prototype fields object");
1132 return super.dateFromFields(fields, options);
1135 yearMonthFromFields(fields, options = {}) {
1136 this.yearMonthFromFieldsCallCount++;
1137 assert.sameValue(Object.getPrototypeOf(fields), null, "yearMonthFromFields should be called with null-prototype fields object");
1138 return super.yearMonthFromFields(fields, options);
1141 monthDayFromFields(fields, options = {}) {
1142 this.monthDayFromFieldsCallCount++;
1143 assert.sameValue(Object.getPrototypeOf(fields), null, "monthDayFromFields should be called with null-prototype fields object");
1144 return super.monthDayFromFields(fields, options);
1148 return new CalendarCheckFieldsPrototypePollution();
1152 * A custom calendar used in prototype pollution checks. Verifies that the
1153 * mergeFields() method is always called with null-prototype fields objects.
1155 calendarCheckMergeFieldsPrototypePollution() {
1156 class CalendarCheckMergeFieldsPrototypePollution extends Temporal.Calendar {
1159 this.mergeFieldsCallCount = 0;
1163 return "merge-fields-null-proto";
1166 mergeFields(fields, additionalFields) {
1167 this.mergeFieldsCallCount++;
1168 assert.sameValue(Object.getPrototypeOf(fields), null, "mergeFields should be called with null-prototype fields object (first argument)");
1169 assert.sameValue(Object.getPrototypeOf(additionalFields), null, "mergeFields should be called with null-prototype fields object (second argument)");
1170 return super.mergeFields(fields, additionalFields);
1174 return new CalendarCheckMergeFieldsPrototypePollution();
1178 * A custom calendar used in prototype pollution checks. Verifies that methods
1179 * are always called with a null-prototype options object.
1181 calendarCheckOptionsPrototypePollution() {
1182 class CalendarCheckOptionsPrototypePollution extends Temporal.Calendar {
1185 this.yearMonthFromFieldsCallCount = 0;
1186 this.dateUntilCallCount = 0;
1190 return "options-null-proto";
1193 yearMonthFromFields(fields, options) {
1194 this.yearMonthFromFieldsCallCount++;
1195 assert.sameValue(Object.getPrototypeOf(options), null, "yearMonthFromFields should be called with null-prototype options");
1196 return super.yearMonthFromFields(fields, options);
1199 dateUntil(one, two, options) {
1200 this.dateUntilCallCount++;
1201 assert.sameValue(Object.getPrototypeOf(options), null, "dateUntil should be called with null-prototype options");
1202 return super.dateUntil(one, two, options);
1206 return new CalendarCheckOptionsPrototypePollution();
1210 * A custom calendar that asserts its dateAdd() method is called with the
1211 * options parameter having the value undefined.
1213 calendarDateAddUndefinedOptions() {
1214 class CalendarDateAddUndefinedOptions extends Temporal.Calendar {
1217 this.dateAddCallCount = 0;
1221 return "dateadd-undef-options";
1224 dateAdd(date, duration, options) {
1225 this.dateAddCallCount++;
1226 assert.sameValue(options, undefined, "dateAdd shouldn't be called with options");
1227 return super.dateAdd(date, duration, options);
1230 return new CalendarDateAddUndefinedOptions();
1234 * A custom calendar that asserts its dateAdd() method is called with a
1235 * PlainDate instance. Optionally, it also asserts that the PlainDate instance
1236 * is the specific object `this.specificPlainDate`, if it is set by the
1239 calendarDateAddPlainDateInstance() {
1240 class CalendarDateAddPlainDateInstance extends Temporal.Calendar {
1243 this.dateAddCallCount = 0;
1244 this.specificPlainDate = undefined;
1248 return "dateadd-plain-date-instance";
1251 dateFromFields(...args) {
1252 return super.dateFromFields(...args).withCalendar(this);
1255 dateAdd(date, duration, options) {
1256 this.dateAddCallCount++;
1257 assert(date instanceof Temporal.PlainDate, "dateAdd() should be called with a PlainDate instance");
1258 if (this.dateAddCallCount === 1 && this.specificPlainDate) {
1259 assert.sameValue(date, this.specificPlainDate, `dateAdd() should be called first with the specific PlainDate instance ${this.specificPlainDate}`);
1261 return super.dateAdd(date, duration, options).withCalendar(this);
1264 return new CalendarDateAddPlainDateInstance();
1268 * A custom calendar that returns an iterable instead of an array from its
1269 * fields() method, otherwise identical to the ISO calendar.
1271 calendarFieldsIterable() {
1272 class CalendarFieldsIterable extends Temporal.Calendar {
1275 this.fieldsCallCount = 0;
1276 this.fieldsCalledWith = [];
1277 this.iteratorExhausted = [];
1281 return "fields-iterable";
1284 fields(fieldNames) {
1285 this.fieldsCallCount++;
1286 this.fieldsCalledWith.push(fieldNames.slice());
1287 this.iteratorExhausted.push(false);
1289 callIndex: this.fieldsCallCount - 1,
1291 *[Symbol.iterator]() {
1292 yield* this.calendar.fieldsCalledWith[this.callIndex];
1293 this.calendar.iteratorExhausted[this.callIndex] = true;
1298 return new CalendarFieldsIterable();
1302 * A custom calendar that asserts its ...FromFields() methods are called with
1303 * the options parameter having the value undefined.
1305 calendarFromFieldsUndefinedOptions() {
1306 class CalendarFromFieldsUndefinedOptions extends Temporal.Calendar {
1309 this.dateFromFieldsCallCount = 0;
1310 this.monthDayFromFieldsCallCount = 0;
1311 this.yearMonthFromFieldsCallCount = 0;
1315 return "from-fields-undef-options";
1318 dateFromFields(fields, options) {
1319 this.dateFromFieldsCallCount++;
1320 assert.sameValue(options, undefined, "dateFromFields shouldn't be called with options");
1321 return super.dateFromFields(fields, options);
1324 yearMonthFromFields(fields, options) {
1325 this.yearMonthFromFieldsCallCount++;
1326 assert.sameValue(options, undefined, "yearMonthFromFields shouldn't be called with options");
1327 return super.yearMonthFromFields(fields, options);
1330 monthDayFromFields(fields, options) {
1331 this.monthDayFromFieldsCallCount++;
1332 assert.sameValue(options, undefined, "monthDayFromFields shouldn't be called with options");
1333 return super.monthDayFromFields(fields, options);
1336 return new CalendarFromFieldsUndefinedOptions();
1340 * A custom calendar that modifies the fields object passed in to
1341 * dateFromFields, sabotaging its time properties.
1343 calendarMakeInfinityTime() {
1344 class CalendarMakeInfinityTime extends Temporal.Calendar {
1349 dateFromFields(fields, options) {
1350 const retval = super.dateFromFields(fields, options);
1351 fields.hour = Infinity;
1352 fields.minute = Infinity;
1353 fields.second = Infinity;
1354 fields.millisecond = Infinity;
1355 fields.microsecond = Infinity;
1356 fields.nanosecond = Infinity;
1360 return new CalendarMakeInfinityTime();
1364 * A custom calendar that defines getters on the fields object passed into
1365 * dateFromFields that throw, sabotaging its time properties.
1367 calendarMakeInvalidGettersTime() {
1368 class CalendarMakeInvalidGettersTime extends Temporal.Calendar {
1373 dateFromFields(fields, options) {
1374 const retval = super.dateFromFields(fields, options);
1375 const throwingDescriptor = {
1377 throw new Test262Error("reading a sabotaged time field");
1380 Object.defineProperties(fields, {
1381 hour: throwingDescriptor,
1382 minute: throwingDescriptor,
1383 second: throwingDescriptor,
1384 millisecond: throwingDescriptor,
1385 microsecond: throwingDescriptor,
1386 nanosecond: throwingDescriptor,
1391 return new CalendarMakeInvalidGettersTime();
1395 * A custom calendar whose mergeFields() method returns a proxy object with
1396 * all of its Get and HasProperty operations observable, as well as adding a
1397 * "shouldNotBeCopied": true property.
1399 calendarMergeFieldsGetters() {
1400 class CalendarMergeFieldsGetters extends Temporal.Calendar {
1403 this.mergeFieldsReturnOperations = [];
1407 return "merge-fields-getters";
1410 dateFromFields(fields, options) {
1411 assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
1412 return super.dateFromFields(fields, options);
1415 yearMonthFromFields(fields, options) {
1416 assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
1417 return super.yearMonthFromFields(fields, options);
1420 monthDayFromFields(fields, options) {
1421 assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
1422 return super.monthDayFromFields(fields, options);
1425 mergeFields(fields, additionalFields) {
1426 const retval = super.mergeFields(fields, additionalFields);
1427 retval._calendar = this;
1428 retval.shouldNotBeCopied = true;
1429 return new Proxy(retval, {
1431 target._calendar.mergeFieldsReturnOperations.push(`get ${key}`);
1432 const result = target[key];
1433 if (result === undefined) {
1436 return TemporalHelpers.toPrimitiveObserver(target._calendar.mergeFieldsReturnOperations, result, key);
1439 target._calendar.mergeFieldsReturnOperations.push(`has ${key}`);
1440 return key in target;
1445 return new CalendarMergeFieldsGetters();
1449 * A custom calendar whose mergeFields() method returns a primitive value,
1450 * given by @primitive, and which records the number of calls made to its
1451 * dateFromFields(), yearMonthFromFields(), and monthDayFromFields() methods.
1453 calendarMergeFieldsReturnsPrimitive(primitive) {
1454 class CalendarMergeFieldsPrimitive extends Temporal.Calendar {
1455 constructor(mergeFieldsReturnValue) {
1457 this._mergeFieldsReturnValue = mergeFieldsReturnValue;
1458 this.dateFromFieldsCallCount = 0;
1459 this.monthDayFromFieldsCallCount = 0;
1460 this.yearMonthFromFieldsCallCount = 0;
1464 return "merge-fields-primitive";
1467 dateFromFields(fields, options) {
1468 this.dateFromFieldsCallCount++;
1469 return super.dateFromFields(fields, options);
1472 yearMonthFromFields(fields, options) {
1473 this.yearMonthFromFieldsCallCount++;
1474 return super.yearMonthFromFields(fields, options);
1477 monthDayFromFields(fields, options) {
1478 this.monthDayFromFieldsCallCount++;
1479 return super.monthDayFromFields(fields, options);
1483 return this._mergeFieldsReturnValue;
1486 return new CalendarMergeFieldsPrimitive(primitive);
1490 * A custom calendar whose fields() method returns the same value as the
1491 * iso8601 calendar, with the addition of extraFields provided as parameter.
1493 calendarWithExtraFields(fields) {
1494 class CalendarWithExtraFields extends Temporal.Calendar {
1495 constructor(extraFields) {
1497 this._extraFields = extraFields;
1500 fields(fieldNames) {
1501 return super.fields(fieldNames).concat(this._extraFields);
1505 return new CalendarWithExtraFields(fields);
1509 * crossDateLineTimeZone():
1511 * This returns an instance of a custom time zone class that implements one
1512 * single transition where the time zone moves from one side of the
1513 * International Date Line to the other, for the purpose of testing time zone
1514 * calculations without depending on system time zone data.
1516 * The transition occurs at epoch second 1325239200 and goes from offset
1517 * -10:00 to +14:00. In other words, the time zone skips the whole calendar
1518 * day of 2011-12-30. This is the same as the real-life transition in the
1519 * Pacific/Apia time zone.
1521 crossDateLineTimeZone() {
1522 const { compare } = Temporal.PlainDate;
1523 const skippedDay = new Temporal.PlainDate(2011, 12, 30);
1524 const transitionEpoch = 1325239200_000_000_000n;
1525 const beforeOffset = new Temporal.TimeZone("-10:00");
1526 const afterOffset = new Temporal.TimeZone("+14:00");
1528 class CrossDateLineTimeZone extends Temporal.TimeZone {
1533 getOffsetNanosecondsFor(instant) {
1534 if (instant.epochNanoseconds < transitionEpoch) {
1535 return beforeOffset.getOffsetNanosecondsFor(instant);
1537 return afterOffset.getOffsetNanosecondsFor(instant);
1540 getPossibleInstantsFor(datetime) {
1541 const comparison = compare(datetime.toPlainDate(), skippedDay);
1542 if (comparison === 0) {
1545 if (comparison < 0) {
1546 return [beforeOffset.getInstantFor(datetime)];
1548 return [afterOffset.getInstantFor(datetime)];
1551 getPreviousTransition(instant) {
1552 if (instant.epochNanoseconds > transitionEpoch) return new Temporal.Instant(transitionEpoch);
1556 getNextTransition(instant) {
1557 if (instant.epochNanoseconds < transitionEpoch) return new Temporal.Instant(transitionEpoch);
1562 return "Custom/Date_Line";
1565 return new CrossDateLineTimeZone();
1569 * observeProperty(calls, object, propertyName, value):
1571 * Defines an own property @object.@propertyName with value @value, that
1572 * will log any calls to its accessors to the array @calls.
1574 observeProperty(calls, object, propertyName, value, objectName = "") {
1575 Object.defineProperty(object, propertyName, {
1577 calls.push(`get ${formatPropertyName(propertyName, objectName)}`);
1581 calls.push(`set ${formatPropertyName(propertyName, objectName)}`);
1587 * observeMethod(calls, object, propertyName, value):
1589 * Defines an own property @object.@propertyName with value @value, that
1590 * will log any calls of @value to the array @calls.
1592 observeMethod(calls, object, propertyName, objectName = "") {
1593 const method = object[propertyName];
1594 object[propertyName] = function () {
1595 calls.push(`call ${formatPropertyName(propertyName, objectName)}`);
1596 return method.apply(object, arguments);
1601 * Used for substituteMethod to indicate default behavior instead of a
1604 SUBSTITUTE_SKIP: SKIP_SYMBOL,
1607 * substituteMethod(object, propertyName, values):
1609 * Defines an own property @object.@propertyName that will, for each
1610 * subsequent call to the method previously defined as
1611 * @object.@propertyName:
1612 * - Call the method, if no more values remain
1613 * - Call the method, if the value in @values for the corresponding call
1614 * is SUBSTITUTE_SKIP
1615 * - Otherwise, return the corresponding value in @value
1617 substituteMethod(object, propertyName, values) {
1619 const method = object[propertyName];
1620 object[propertyName] = function () {
1621 if (calls >= values.length) {
1622 return method.apply(object, arguments);
1623 } else if (values[calls] === SKIP_SYMBOL) {
1625 return method.apply(object, arguments);
1627 return values[calls++];
1634 * A custom calendar that behaves exactly like the ISO 8601 calendar but
1635 * tracks calls to any of its methods, and Get/Has operations on its
1636 * properties, by appending messages to an array. This is for the purpose of
1637 * testing order of operations that are observable from user code.
1638 * objectName is used in the log.
1640 calendarObserver(calls, objectName, methodOverrides = {}) {
1641 function removeExtraHasPropertyChecks(objectName, calls) {
1642 // Inserting the tracking calendar into the return values of methods
1643 // that we chain up into the ISO calendar for, causes extra HasProperty
1644 // checks, which we observe. This removes them so that we don't leak
1645 // implementation details of the helper into the test code.
1646 assert.sameValue(calls.pop(), `has ${objectName}.yearOfWeek`);
1647 assert.sameValue(calls.pop(), `has ${objectName}.yearMonthFromFields`);
1648 assert.sameValue(calls.pop(), `has ${objectName}.year`);
1649 assert.sameValue(calls.pop(), `has ${objectName}.weekOfYear`);
1650 assert.sameValue(calls.pop(), `has ${objectName}.monthsInYear`);
1651 assert.sameValue(calls.pop(), `has ${objectName}.monthDayFromFields`);
1652 assert.sameValue(calls.pop(), `has ${objectName}.monthCode`);
1653 assert.sameValue(calls.pop(), `has ${objectName}.month`);
1654 assert.sameValue(calls.pop(), `has ${objectName}.mergeFields`);
1655 assert.sameValue(calls.pop(), `has ${objectName}.inLeapYear`);
1656 assert.sameValue(calls.pop(), `has ${objectName}.id`);
1657 assert.sameValue(calls.pop(), `has ${objectName}.fields`);
1658 assert.sameValue(calls.pop(), `has ${objectName}.daysInYear`);
1659 assert.sameValue(calls.pop(), `has ${objectName}.daysInWeek`);
1660 assert.sameValue(calls.pop(), `has ${objectName}.daysInMonth`);
1661 assert.sameValue(calls.pop(), `has ${objectName}.dayOfYear`);
1662 assert.sameValue(calls.pop(), `has ${objectName}.dayOfWeek`);
1663 assert.sameValue(calls.pop(), `has ${objectName}.day`);
1664 assert.sameValue(calls.pop(), `has ${objectName}.dateUntil`);
1665 assert.sameValue(calls.pop(), `has ${objectName}.dateFromFields`);
1666 assert.sameValue(calls.pop(), `has ${objectName}.dateAdd`);
1669 const iso8601 = new Temporal.Calendar("iso8601");
1670 const trackingMethods = {
1671 dateFromFields(...args) {
1672 calls.push(`call ${objectName}.dateFromFields`);
1673 if ('dateFromFields' in methodOverrides) {
1674 const value = methodOverrides.dateFromFields;
1675 return typeof value === "function" ? value(...args) : value;
1677 const originalResult = iso8601.dateFromFields(...args);
1678 // Replace the calendar in the result with the call-tracking calendar
1679 const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
1680 const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this);
1681 removeExtraHasPropertyChecks(objectName, calls);
1684 yearMonthFromFields(...args) {
1685 calls.push(`call ${objectName}.yearMonthFromFields`);
1686 if ('yearMonthFromFields' in methodOverrides) {
1687 const value = methodOverrides.yearMonthFromFields;
1688 return typeof value === "function" ? value(...args) : value;
1690 const originalResult = iso8601.yearMonthFromFields(...args);
1691 // Replace the calendar in the result with the call-tracking calendar
1692 const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
1693 const result = new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
1694 removeExtraHasPropertyChecks(objectName, calls);
1697 monthDayFromFields(...args) {
1698 calls.push(`call ${objectName}.monthDayFromFields`);
1699 if ('monthDayFromFields' in methodOverrides) {
1700 const value = methodOverrides.monthDayFromFields;
1701 return typeof value === "function" ? value(...args) : value;
1703 const originalResult = iso8601.monthDayFromFields(...args);
1704 // Replace the calendar in the result with the call-tracking calendar
1705 const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
1706 const result = new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
1707 removeExtraHasPropertyChecks(objectName, calls);
1711 calls.push(`call ${objectName}.dateAdd`);
1712 if ('dateAdd' in methodOverrides) {
1713 const value = methodOverrides.dateAdd;
1714 return typeof value === "function" ? value(...args) : value;
1716 const originalResult = iso8601.dateAdd(...args);
1717 const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
1718 const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this);
1719 removeExtraHasPropertyChecks(objectName, calls);
1724 // Automatically generate the other methods that don't need any custom code
1745 ].forEach((methodName) => {
1746 trackingMethods[methodName] = function (...args) {
1747 calls.push(`call ${formatPropertyName(methodName, objectName)}`);
1748 if (methodName in methodOverrides) {
1749 const value = methodOverrides[methodName];
1750 return typeof value === "function" ? value(...args) : value;
1752 return iso8601[methodName](...args);
1755 return new Proxy(trackingMethods, {
1756 get(target, key, receiver) {
1757 const result = Reflect.get(target, key, receiver);
1758 calls.push(`get ${formatPropertyName(key, objectName)}`);
1762 calls.push(`has ${formatPropertyName(key, objectName)}`);
1763 return Reflect.has(target, key);
1769 * A custom calendar that does not allow any of its methods to be called, for
1770 * the purpose of asserting that a particular operation does not call into
1773 calendarThrowEverything() {
1774 class CalendarThrowEverything extends Temporal.Calendar {
1779 TemporalHelpers.assertUnreachable("toString should not be called");
1782 TemporalHelpers.assertUnreachable("dateFromFields should not be called");
1784 yearMonthFromFields() {
1785 TemporalHelpers.assertUnreachable("yearMonthFromFields should not be called");
1787 monthDayFromFields() {
1788 TemporalHelpers.assertUnreachable("monthDayFromFields should not be called");
1791 TemporalHelpers.assertUnreachable("dateAdd should not be called");
1794 TemporalHelpers.assertUnreachable("dateUntil should not be called");
1797 TemporalHelpers.assertUnreachable("era should not be called");
1800 TemporalHelpers.assertUnreachable("eraYear should not be called");
1803 TemporalHelpers.assertUnreachable("year should not be called");
1806 TemporalHelpers.assertUnreachable("month should not be called");
1809 TemporalHelpers.assertUnreachable("monthCode should not be called");
1812 TemporalHelpers.assertUnreachable("day should not be called");
1815 TemporalHelpers.assertUnreachable("fields should not be called");
1818 TemporalHelpers.assertUnreachable("mergeFields should not be called");
1822 return new CalendarThrowEverything();
1826 * oneShiftTimeZone(shiftInstant, shiftNanoseconds):
1828 * In the case of a spring-forward time zone offset transition (skipped time),
1829 * and disambiguation === 'earlier', BuiltinTimeZoneGetInstantFor subtracts a
1830 * negative number of nanoseconds from a PlainDateTime, which should balance
1831 * with the microseconds field.
1833 * This returns an instance of a custom time zone class which skips a length
1834 * of time equal to shiftNanoseconds (a number), at the Temporal.Instant
1835 * shiftInstant. Before shiftInstant, it's identical to UTC, and after
1836 * shiftInstant it's a constant-offset time zone.
1838 * It provides a getPossibleInstantsForCalledWith member which is an array
1839 * with the result of calling toString() on any PlainDateTimes passed to
1840 * getPossibleInstantsFor().
1842 oneShiftTimeZone(shiftInstant, shiftNanoseconds) {
1843 class OneShiftTimeZone extends Temporal.TimeZone {
1844 constructor(shiftInstant, shiftNanoseconds) {
1846 this._shiftInstant = shiftInstant;
1847 this._epoch1 = shiftInstant.epochNanoseconds;
1848 this._epoch2 = this._epoch1 + BigInt(shiftNanoseconds);
1849 this._shiftNanoseconds = shiftNanoseconds;
1850 this._shift = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, this._shiftNanoseconds);
1851 this.getPossibleInstantsForCalledWith = [];
1854 _isBeforeShift(instant) {
1855 return instant.epochNanoseconds < this._epoch1;
1858 getOffsetNanosecondsFor(instant) {
1859 return this._isBeforeShift(instant) ? 0 : this._shiftNanoseconds;
1862 getPossibleInstantsFor(plainDateTime) {
1863 this.getPossibleInstantsForCalledWith.push(plainDateTime.toString({ calendarName: "never" }));
1864 const [instant] = super.getPossibleInstantsFor(plainDateTime);
1865 if (this._shiftNanoseconds > 0) {
1866 if (this._isBeforeShift(instant)) return [instant];
1867 if (instant.epochNanoseconds < this._epoch2) return [];
1868 return [instant.subtract(this._shift)];
1870 if (instant.epochNanoseconds < this._epoch2) return [instant];
1871 const shifted = instant.subtract(this._shift);
1872 if (this._isBeforeShift(instant)) return [instant, shifted];
1876 getNextTransition(instant) {
1877 return this._isBeforeShift(instant) ? this._shiftInstant : null;
1880 getPreviousTransition(instant) {
1881 return this._isBeforeShift(instant) ? null : this._shiftInstant;
1885 return "Custom/One_Shift";
1888 return new OneShiftTimeZone(shiftInstant, shiftNanoseconds);
1892 * propertyBagObserver():
1893 * Returns an object that behaves like the given propertyBag but tracks Get
1894 * and Has operations on any of its properties, by appending messages to an
1895 * array. If the value of a property in propertyBag is a primitive, the value
1896 * of the returned object's property will additionally be a
1897 * TemporalHelpers.toPrimitiveObserver that will track calls to its toString
1898 * and valueOf methods in the same array. This is for the purpose of testing
1899 * order of operations that are observable from user code. objectName is used
1902 propertyBagObserver(calls, propertyBag, objectName) {
1903 return new Proxy(propertyBag, {
1905 calls.push(`ownKeys ${objectName}`);
1906 return Reflect.ownKeys(target);
1908 getOwnPropertyDescriptor(target, key) {
1909 calls.push(`getOwnPropertyDescriptor ${formatPropertyName(key, objectName)}`);
1910 return Reflect.getOwnPropertyDescriptor(target, key);
1912 get(target, key, receiver) {
1913 calls.push(`get ${formatPropertyName(key, objectName)}`);
1914 const result = Reflect.get(target, key, receiver);
1915 if (result === undefined) {
1918 if ((result !== null && typeof result === "object") || typeof result === "function") {
1921 return TemporalHelpers.toPrimitiveObserver(calls, result, `${formatPropertyName(key, objectName)}`);
1924 calls.push(`has ${formatPropertyName(key, objectName)}`);
1925 return Reflect.has(target, key);
1931 * specificOffsetTimeZone():
1933 * This returns an instance of a custom time zone class, which returns a
1934 * specific custom value from its getOffsetNanosecondsFrom() method. This is
1935 * for the purpose of testing the validation of what this method returns.
1937 * It also returns an empty array from getPossibleInstantsFor(), so as to
1938 * trigger calls to getOffsetNanosecondsFor() when used from the
1939 * BuiltinTimeZoneGetInstantFor operation.
1941 specificOffsetTimeZone(offsetValue) {
1942 class SpecificOffsetTimeZone extends Temporal.TimeZone {
1943 constructor(offsetValue) {
1945 this._offsetValue = offsetValue;
1948 getOffsetNanosecondsFor() {
1949 return this._offsetValue;
1952 getPossibleInstantsFor(dt) {
1953 if (typeof this._offsetValue !== 'number' || Math.abs(this._offsetValue) >= 86400e9 || isNaN(this._offsetValue)) return [];
1954 const zdt = dt.toZonedDateTime("UTC").add({ nanoseconds: -this._offsetValue });
1955 return [zdt.toInstant()];
1959 return this.getOffsetStringFor(new Temporal.Instant(0n));
1962 return new SpecificOffsetTimeZone(offsetValue);
1966 * springForwardFallBackTimeZone():
1968 * This returns an instance of a custom time zone class that implements one
1969 * single spring-forward/fall-back transition, for the purpose of testing the
1970 * disambiguation option, without depending on system time zone data.
1972 * The spring-forward occurs at epoch second 954669600 (2000-04-02T02:00
1973 * local) and goes from offset -08:00 to -07:00.
1975 * The fall-back occurs at epoch second 972810000 (2000-10-29T02:00 local) and
1976 * goes from offset -07:00 to -08:00.
1978 springForwardFallBackTimeZone() {
1979 const { compare } = Temporal.PlainDateTime;
1980 const springForwardLocal = new Temporal.PlainDateTime(2000, 4, 2, 2);
1981 const springForwardEpoch = 954669600_000_000_000n;
1982 const fallBackLocal = new Temporal.PlainDateTime(2000, 10, 29, 1);
1983 const fallBackEpoch = 972810000_000_000_000n;
1984 const winterOffset = new Temporal.TimeZone('-08:00');
1985 const summerOffset = new Temporal.TimeZone('-07:00');
1987 class SpringForwardFallBackTimeZone extends Temporal.TimeZone {
1992 getOffsetNanosecondsFor(instant) {
1993 if (instant.epochNanoseconds < springForwardEpoch ||
1994 instant.epochNanoseconds >= fallBackEpoch) {
1995 return winterOffset.getOffsetNanosecondsFor(instant);
1997 return summerOffset.getOffsetNanosecondsFor(instant);
2000 getPossibleInstantsFor(datetime) {
2001 if (compare(datetime, springForwardLocal) >= 0 && compare(datetime, springForwardLocal.add({ hours: 1 })) < 0) {
2004 if (compare(datetime, fallBackLocal) >= 0 && compare(datetime, fallBackLocal.add({ hours: 1 })) < 0) {
2005 return [summerOffset.getInstantFor(datetime), winterOffset.getInstantFor(datetime)];
2007 if (compare(datetime, springForwardLocal) < 0 || compare(datetime, fallBackLocal) >= 0) {
2008 return [winterOffset.getInstantFor(datetime)];
2010 return [summerOffset.getInstantFor(datetime)];
2013 getPreviousTransition(instant) {
2014 if (instant.epochNanoseconds > fallBackEpoch) return new Temporal.Instant(fallBackEpoch);
2015 if (instant.epochNanoseconds > springForwardEpoch) return new Temporal.Instant(springForwardEpoch);
2019 getNextTransition(instant) {
2020 if (instant.epochNanoseconds < springForwardEpoch) return new Temporal.Instant(springForwardEpoch);
2021 if (instant.epochNanoseconds < fallBackEpoch) return new Temporal.Instant(fallBackEpoch);
2026 return "Custom/Spring_Fall";
2030 return "Custom/Spring_Fall";
2033 return new SpringForwardFallBackTimeZone();
2038 * A custom calendar that behaves exactly like the UTC time zone but tracks
2039 * calls to any of its methods, and Get/Has operations on its properties, by
2040 * appending messages to an array. This is for the purpose of testing order of
2041 * operations that are observable from user code. objectName is used in the
2042 * log. methodOverrides is an optional object containing properties with the
2043 * same name as Temporal.TimeZone methods. If the property value is a function
2044 * it will be called with the proper arguments instead of the UTC method.
2045 * Otherwise, the property value will be returned directly.
2047 timeZoneObserver(calls, objectName, methodOverrides = {}) {
2048 const utc = new Temporal.TimeZone("UTC");
2049 const trackingMethods = {
2052 // Automatically generate the methods
2053 ["getOffsetNanosecondsFor", "getPossibleInstantsFor", "toString"].forEach((methodName) => {
2054 trackingMethods[methodName] = function (...args) {
2055 calls.push(`call ${formatPropertyName(methodName, objectName)}`);
2056 if (methodName in methodOverrides) {
2057 const value = methodOverrides[methodName];
2058 return typeof value === "function" ? value(...args) : value;
2060 return utc[methodName](...args);
2063 return new Proxy(trackingMethods, {
2064 get(target, key, receiver) {
2065 const result = Reflect.get(target, key, receiver);
2066 calls.push(`get ${formatPropertyName(key, objectName)}`);
2070 calls.push(`has ${formatPropertyName(key, objectName)}`);
2071 return Reflect.has(target, key);
2077 * A custom time zone that does not allow any of its methods to be called, for
2078 * the purpose of asserting that a particular operation does not call into
2081 timeZoneThrowEverything() {
2082 class TimeZoneThrowEverything extends Temporal.TimeZone {
2086 getOffsetNanosecondsFor() {
2087 TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be called");
2089 getPossibleInstantsFor() {
2090 TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be called");
2093 TemporalHelpers.assertUnreachable("toString should not be called");
2097 return new TimeZoneThrowEverything();
2101 * Returns an object that will append logs of any Gets or Calls of its valueOf
2102 * or toString properties to the array calls. Both valueOf and toString will
2103 * return the actual primitiveValue. propertyName is used in the log.
2105 toPrimitiveObserver(calls, primitiveValue, propertyName) {
2108 calls.push(`get ${propertyName}.valueOf`);
2109 return function () {
2110 calls.push(`call ${propertyName}.valueOf`);
2111 return primitiveValue;
2115 calls.push(`get ${propertyName}.toString`);
2116 return function () {
2117 calls.push(`call ${propertyName}.toString`);
2118 if (primitiveValue === undefined) return undefined;
2119 return primitiveValue.toString();
2126 * An object containing further methods that return arrays of ISO strings, for
2131 * PlainMonthDay strings that are not valid.
2133 plainMonthDayStringsInvalid() {
2136 "11-18[u-ca=gregory]",
2137 "11-18[u-ca=hebrew]",
2142 * PlainMonthDay strings that are valid and that should produce October 1st.
2144 plainMonthDayStringsValid() {
2149 "1976-10-01T152330.1+00:00",
2150 "19761001T15:23:30.1+00:00",
2151 "1976-10-01T15:23:30.1+0000",
2152 "1976-10-01T152330.1+0000",
2153 "19761001T15:23:30.1+0000",
2154 "19761001T152330.1+00:00",
2155 "19761001T152330.1+0000",
2156 "+001976-10-01T152330.1+00:00",
2157 "+0019761001T15:23:30.1+00:00",
2158 "+001976-10-01T15:23:30.1+0000",
2159 "+001976-10-01T152330.1+0000",
2160 "+0019761001T15:23:30.1+0000",
2161 "+0019761001T152330.1+00:00",
2162 "+0019761001T152330.1+0000",
2163 "1976-10-01T15:23:00",
2173 * PlainTime strings that may be mistaken for PlainMonthDay or
2174 * PlainYearMonth strings, and so require a time designator.
2176 plainTimeStringsAmbiguous() {
2177 const ambiguousStrings = [
2178 "2021-12", // ambiguity between YYYY-MM and HHMM-UU
2179 "2021-12[-12:00]", // ditto, TZ does not disambiguate
2180 "1214", // ambiguity between MMDD and HHMM
2181 "0229", // ditto, including MMDD that doesn't occur every year
2182 "1130", // ditto, including DD that doesn't occur in every month
2183 "12-14", // ambiguity between MM-DD and HH-UU
2184 "12-14[-14:00]", // ditto, TZ does not disambiguate
2185 "202112", // ambiguity between YYYYMM and HHMMSS
2186 "202112[UTC]", // ditto, TZ does not disambiguate
2188 // Adding a calendar annotation to one of these strings must not cause
2189 // disambiguation in favour of time.
2190 const stringsWithCalendar = ambiguousStrings.map((s) => s + '[u-ca=iso8601]');
2191 return ambiguousStrings.concat(stringsWithCalendar);
2195 * PlainTime strings that are of similar form to PlainMonthDay and
2196 * PlainYearMonth strings, but are not ambiguous due to components that
2197 * aren't valid as months or days.
2199 plainTimeStringsUnambiguous() {
2201 "2021-13", // 13 is not a month
2203 "2021-13[-13:00]", // ditto
2204 "202113[-13:00]", // ditto
2205 "0000-00", // 0 is not a month
2207 "0000-00[UTC]", // ditto
2208 "000000[UTC]", // ditto
2209 "1314", // 13 is not a month
2211 "1232", // 32 is not a day
2212 "0230", // 30 is not a day in February
2213 "0631", // 31 is not a day in June
2214 "0000", // 0 is neither a month nor a day
2220 * PlainYearMonth-like strings that are not valid.
2222 plainYearMonthStringsInvalid() {
2229 * PlainYearMonth-like strings that are valid and should produce November
2230 * 1976 in the ISO 8601 calendar.
2232 plainYearMonthStringsValid() {
2236 "1976-11-01T09:00:00+00:00",
2237 "1976-11-01T00:00:00+05:00",
2240 "1976-11-18T15:23:30.1\u221202:00",
2241 "1976-11-18T152330.1+00:00",
2242 "19761118T15:23:30.1+00:00",
2243 "1976-11-18T15:23:30.1+0000",
2244 "1976-11-18T152330.1+0000",
2245 "19761118T15:23:30.1+0000",
2246 "19761118T152330.1+00:00",
2247 "19761118T152330.1+0000",
2248 "+001976-11-18T152330.1+00:00",
2249 "+0019761118T15:23:30.1+00:00",
2250 "+001976-11-18T15:23:30.1+0000",
2251 "+001976-11-18T152330.1+0000",
2252 "+0019761118T15:23:30.1+0000",
2253 "+0019761118T152330.1+00:00",
2254 "+0019761118T152330.1+0000",
2262 * PlainYearMonth-like strings that are valid and should produce November of
2263 * the ISO year -9999.
2265 plainYearMonthStringsValidNegativeYear() {