1 // GENERATED, DO NOT EDIT
2 // file: isConstructor.js
3 // Copyright (C) 2017 André Bargull. All rights reserved.
4 // This code is governed by the BSD license found in the LICENSE file.
8 Test if a given function is a constructor function.
9 defines: [isConstructor]
10 features: [Reflect.construct]
13 function isConstructor(f) {
14 if (typeof f !== "function") {
15 throw new Test262Error("isConstructor invoked with a non-function value");
19 Reflect.construct(function(){}, [], f);
26 // file: temporalHelpers.js
27 // Copyright (C) 2021 Igalia, S.L. All rights reserved.
28 // This code is governed by the BSD license found in the LICENSE file.
31 This defines helper objects and functions for testing Temporal.
32 defines: [TemporalHelpers]
33 features: [Symbol.species, Symbol.iterator, Temporal]
36 const ASCII_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/u;
38 function formatPropertyName(propertyKey, objectName = "") {
39 switch (typeof propertyKey) {
41 if (Symbol.keyFor(propertyKey) !== undefined) {
42 return `${objectName}[Symbol.for('${Symbol.keyFor(propertyKey)}')]`;
43 } else if (propertyKey.description.startsWith('Symbol.')) {
44 return `${objectName}[${propertyKey.description}]`;
46 return `${objectName}[Symbol('${propertyKey.description}')]`
49 if (propertyKey !== String(Number(propertyKey))) {
50 if (ASCII_IDENTIFIER.test(propertyKey)) {
51 return objectName ? `${objectName}.${propertyKey}` : propertyKey;
53 return `${objectName}['${propertyKey.replace(/'/g, "\\'")}']`
57 // integer or string integer-index
58 return `${objectName}[${propertyKey}]`;
62 const SKIP_SYMBOL = Symbol("Skip");
64 var TemporalHelpers = {
66 * Codes and maximum lengths of months in the ISO 8601 calendar.
69 { month: 1, monthCode: "M01", daysInMonth: 31 },
70 { month: 2, monthCode: "M02", daysInMonth: 29 },
71 { month: 3, monthCode: "M03", daysInMonth: 31 },
72 { month: 4, monthCode: "M04", daysInMonth: 30 },
73 { month: 5, monthCode: "M05", daysInMonth: 31 },
74 { month: 6, monthCode: "M06", daysInMonth: 30 },
75 { month: 7, monthCode: "M07", daysInMonth: 31 },
76 { month: 8, monthCode: "M08", daysInMonth: 31 },
77 { month: 9, monthCode: "M09", daysInMonth: 30 },
78 { month: 10, monthCode: "M10", daysInMonth: 31 },
79 { month: 11, monthCode: "M11", daysInMonth: 30 },
80 { month: 12, monthCode: "M12", daysInMonth: 31 }
84 * assertDuration(duration, years, ..., nanoseconds[, description]):
86 * Shorthand for asserting that each field of a Temporal.Duration is equal to
89 assertDuration(duration, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, description = "") {
90 const prefix = description ? `${description}: ` : "";
91 assert(duration instanceof Temporal.Duration, `${prefix}instanceof`);
92 assert.sameValue(duration.years, years, `${prefix}years result:`);
93 assert.sameValue(duration.months, months, `${prefix}months result:`);
94 assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`);
95 assert.sameValue(duration.days, days, `${prefix}days result:`);
96 assert.sameValue(duration.hours, hours, `${prefix}hours result:`);
97 assert.sameValue(duration.minutes, minutes, `${prefix}minutes result:`);
98 assert.sameValue(duration.seconds, seconds, `${prefix}seconds result:`);
99 assert.sameValue(duration.milliseconds, milliseconds, `${prefix}milliseconds result:`);
100 assert.sameValue(duration.microseconds, microseconds, `${prefix}microseconds result:`);
101 assert.sameValue(duration.nanoseconds, nanoseconds, `${prefix}nanoseconds result`);
105 * assertDateDuration(duration, years, months, weeks, days, [, description]):
107 * Shorthand for asserting that each date field of a Temporal.Duration is
108 * equal to an expected value.
110 assertDateDuration(duration, years, months, weeks, days, description = "") {
111 const prefix = description ? `${description}: ` : "";
112 assert(duration instanceof Temporal.Duration, `${prefix}instanceof`);
113 assert.sameValue(duration.years, years, `${prefix}years result:`);
114 assert.sameValue(duration.months, months, `${prefix}months result:`);
115 assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`);
116 assert.sameValue(duration.days, days, `${prefix}days result:`);
117 assert.sameValue(duration.hours, 0, `${prefix}hours result should be zero:`);
118 assert.sameValue(duration.minutes, 0, `${prefix}minutes result should be zero:`);
119 assert.sameValue(duration.seconds, 0, `${prefix}seconds result should be zero:`);
120 assert.sameValue(duration.milliseconds, 0, `${prefix}milliseconds result should be zero:`);
121 assert.sameValue(duration.microseconds, 0, `${prefix}microseconds result should be zero:`);
122 assert.sameValue(duration.nanoseconds, 0, `${prefix}nanoseconds result should be zero:`);
126 * assertDurationsEqual(actual, expected[, description]):
128 * Shorthand for asserting that each field of a Temporal.Duration is equal to
129 * the corresponding field in another Temporal.Duration.
131 assertDurationsEqual(actual, expected, description = "") {
132 const prefix = description ? `${description}: ` : "";
133 assert(expected instanceof Temporal.Duration, `${prefix}expected value should be a Temporal.Duration`);
134 TemporalHelpers.assertDuration(actual, expected.years, expected.months, expected.weeks, expected.days, expected.hours, expected.minutes, expected.seconds, expected.milliseconds, expected.microseconds, expected.nanoseconds, description);
138 * assertInstantsEqual(actual, expected[, description]):
140 * Shorthand for asserting that two Temporal.Instants are of the correct type
141 * and equal according to their equals() methods.
143 assertInstantsEqual(actual, expected, description = "") {
144 const prefix = description ? `${description}: ` : "";
145 assert(expected instanceof Temporal.Instant, `${prefix}expected value should be a Temporal.Instant`);
146 assert(actual instanceof Temporal.Instant, `${prefix}instanceof`);
147 assert(actual.equals(expected), `${prefix}equals method`);
151 * assertPlainDate(date, year, ..., nanosecond[, description[, era, eraYear]]):
153 * Shorthand for asserting that each field of a Temporal.PlainDate is equal to
154 * an expected value. (Except the `calendar` property, since callers may want
155 * to assert either object equality with an object they put in there, or the
156 * value of date.calendarId.)
158 assertPlainDate(date, year, month, monthCode, day, description = "", era = undefined, eraYear = undefined) {
159 const prefix = description ? `${description}: ` : "";
160 assert(date instanceof Temporal.PlainDate, `${prefix}instanceof`);
161 assert.sameValue(date.era, era, `${prefix}era result:`);
162 assert.sameValue(date.eraYear, eraYear, `${prefix}eraYear result:`);
163 assert.sameValue(date.year, year, `${prefix}year result:`);
164 assert.sameValue(date.month, month, `${prefix}month result:`);
165 assert.sameValue(date.monthCode, monthCode, `${prefix}monthCode result:`);
166 assert.sameValue(date.day, day, `${prefix}day result:`);
170 * assertPlainDateTime(datetime, year, ..., nanosecond[, description[, era, eraYear]]):
172 * Shorthand for asserting that each field of a Temporal.PlainDateTime is
173 * equal to an expected value. (Except the `calendar` property, since callers
174 * may want to assert either object equality with an object they put in there,
175 * or the value of datetime.calendarId.)
177 assertPlainDateTime(datetime, year, month, monthCode, day, hour, minute, second, millisecond, microsecond, nanosecond, description = "", era = undefined, eraYear = undefined) {
178 const prefix = description ? `${description}: ` : "";
179 assert(datetime instanceof Temporal.PlainDateTime, `${prefix}instanceof`);
180 assert.sameValue(datetime.era, era, `${prefix}era result:`);
181 assert.sameValue(datetime.eraYear, eraYear, `${prefix}eraYear result:`);
182 assert.sameValue(datetime.year, year, `${prefix}year result:`);
183 assert.sameValue(datetime.month, month, `${prefix}month result:`);
184 assert.sameValue(datetime.monthCode, monthCode, `${prefix}monthCode result:`);
185 assert.sameValue(datetime.day, day, `${prefix}day result:`);
186 assert.sameValue(datetime.hour, hour, `${prefix}hour result:`);
187 assert.sameValue(datetime.minute, minute, `${prefix}minute result:`);
188 assert.sameValue(datetime.second, second, `${prefix}second result:`);
189 assert.sameValue(datetime.millisecond, millisecond, `${prefix}millisecond result:`);
190 assert.sameValue(datetime.microsecond, microsecond, `${prefix}microsecond result:`);
191 assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`);
195 * assertPlainDateTimesEqual(actual, expected[, description]):
197 * Shorthand for asserting that two Temporal.PlainDateTimes are of the correct
198 * type, equal according to their equals() methods, and additionally that
199 * their calendar internal slots are the same value.
201 assertPlainDateTimesEqual(actual, expected, description = "") {
202 const prefix = description ? `${description}: ` : "";
203 assert(expected instanceof Temporal.PlainDateTime, `${prefix}expected value should be a Temporal.PlainDateTime`);
204 assert(actual instanceof Temporal.PlainDateTime, `${prefix}instanceof`);
205 assert(actual.equals(expected), `${prefix}equals method`);
207 actual.getISOFields().calendar,
208 expected.getISOFields().calendar,
209 `${prefix}calendar same value:`
214 * assertPlainMonthDay(monthDay, monthCode, day[, description [, referenceISOYear]]):
216 * Shorthand for asserting that each field of a Temporal.PlainMonthDay is
217 * equal to an expected value. (Except the `calendar` property, since callers
218 * may want to assert either object equality with an object they put in there,
219 * or the value of monthDay.calendarId().)
221 assertPlainMonthDay(monthDay, monthCode, day, description = "", referenceISOYear = 1972) {
222 const prefix = description ? `${description}: ` : "";
223 assert(monthDay instanceof Temporal.PlainMonthDay, `${prefix}instanceof`);
224 assert.sameValue(monthDay.monthCode, monthCode, `${prefix}monthCode result:`);
225 assert.sameValue(monthDay.day, day, `${prefix}day result:`);
226 assert.sameValue(monthDay.getISOFields().isoYear, referenceISOYear, `${prefix}referenceISOYear result:`);
230 * assertPlainTime(time, hour, ..., nanosecond[, description]):
232 * Shorthand for asserting that each field of a Temporal.PlainTime is equal to
235 assertPlainTime(time, hour, minute, second, millisecond, microsecond, nanosecond, description = "") {
236 const prefix = description ? `${description}: ` : "";
237 assert(time instanceof Temporal.PlainTime, `${prefix}instanceof`);
238 assert.sameValue(time.hour, hour, `${prefix}hour result:`);
239 assert.sameValue(time.minute, minute, `${prefix}minute result:`);
240 assert.sameValue(time.second, second, `${prefix}second result:`);
241 assert.sameValue(time.millisecond, millisecond, `${prefix}millisecond result:`);
242 assert.sameValue(time.microsecond, microsecond, `${prefix}microsecond result:`);
243 assert.sameValue(time.nanosecond, nanosecond, `${prefix}nanosecond result:`);
247 * assertPlainTimesEqual(actual, expected[, description]):
249 * Shorthand for asserting that two Temporal.PlainTimes are of the correct
250 * type and equal according to their equals() methods.
252 assertPlainTimesEqual(actual, expected, description = "") {
253 const prefix = description ? `${description}: ` : "";
254 assert(expected instanceof Temporal.PlainTime, `${prefix}expected value should be a Temporal.PlainTime`);
255 assert(actual instanceof Temporal.PlainTime, `${prefix}instanceof`);
256 assert(actual.equals(expected), `${prefix}equals method`);
260 * assertPlainYearMonth(yearMonth, year, month, monthCode[, description[, era, eraYear, referenceISODay]]):
262 * Shorthand for asserting that each field of a Temporal.PlainYearMonth is
263 * equal to an expected value. (Except the `calendar` property, since callers
264 * may want to assert either object equality with an object they put in there,
265 * or the value of yearMonth.calendarId.)
267 assertPlainYearMonth(yearMonth, year, month, monthCode, description = "", era = undefined, eraYear = undefined, referenceISODay = 1) {
268 const prefix = description ? `${description}: ` : "";
269 assert(yearMonth instanceof Temporal.PlainYearMonth, `${prefix}instanceof`);
270 assert.sameValue(yearMonth.era, era, `${prefix}era result:`);
271 assert.sameValue(yearMonth.eraYear, eraYear, `${prefix}eraYear result:`);
272 assert.sameValue(yearMonth.year, year, `${prefix}year result:`);
273 assert.sameValue(yearMonth.month, month, `${prefix}month result:`);
274 assert.sameValue(yearMonth.monthCode, monthCode, `${prefix}monthCode result:`);
275 assert.sameValue(yearMonth.getISOFields().isoDay, referenceISODay, `${prefix}referenceISODay result:`);
279 * assertZonedDateTimesEqual(actual, expected[, description]):
281 * Shorthand for asserting that two Temporal.ZonedDateTimes are of the correct
282 * type, equal according to their equals() methods, and additionally that
283 * their time zones and calendar internal slots are the same value.
285 assertZonedDateTimesEqual(actual, expected, description = "") {
286 const prefix = description ? `${description}: ` : "";
287 assert(expected instanceof Temporal.ZonedDateTime, `${prefix}expected value should be a Temporal.ZonedDateTime`);
288 assert(actual instanceof Temporal.ZonedDateTime, `${prefix}instanceof`);
289 assert(actual.equals(expected), `${prefix}equals method`);
290 assert.sameValue(actual.timeZone, expected.timeZone, `${prefix}time zone same value:`);
292 actual.getISOFields().calendar,
293 expected.getISOFields().calendar,
294 `${prefix}calendar same value:`
299 * assertUnreachable(description):
301 * Helper for asserting that code is not executed. This is useful for
302 * assertions that methods of user calendars and time zones are not called.
304 assertUnreachable(description) {
305 let message = "This code should not be executed";
307 message = `${message}: ${description}`;
309 throw new Test262Error(message);
313 * checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls):
315 * When an options object with a largestUnit property is synthesized inside
316 * Temporal and passed to user code such as calendar.dateUntil(), the value of
317 * the largestUnit property should be in the singular form, even if the input
318 * was given in the plural form.
319 * (This doesn't apply when the options object is passed through verbatim.)
321 * func(calendar, largestUnit, index) is the operation under test. It's called
322 * with an instance of a calendar that keeps track of which largestUnit is
323 * passed to dateUntil(), each key of expectedLargestUnitCalls in turn, and
324 * the key's numerical index in case the function needs to generate test data
325 * based on the index. At the end, the actual values passed to dateUntil() are
326 * compared with the array values of expectedLargestUnitCalls.
328 checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls) {
331 class DateUntilOptionsCalendar extends Temporal.Calendar {
336 dateUntil(earlier, later, options) {
337 actual.push(options.largestUnit);
338 return super.dateUntil(earlier, later, options);
342 return "date-until-options";
346 const calendar = new DateUntilOptionsCalendar();
347 Object.entries(expectedLargestUnitCalls).forEach(([largestUnit, expected], index) => {
348 func(calendar, largestUnit, index);
349 assert.compareArray(actual, expected, `largestUnit passed to calendar.dateUntil() for largestUnit ${largestUnit}`);
350 actual.splice(0); // empty it for the next check
355 * checkPlainDateTimeConversionFastPath(func):
357 * ToTemporalDate and ToTemporalTime should both, if given a
358 * Temporal.PlainDateTime instance, convert to the desired type by reading the
359 * PlainDateTime's internal slots, rather than calling any getters.
361 * func(datetime, calendar) is the actual operation to test, that must
362 * internally call the abstract operation ToTemporalDate or ToTemporalTime.
363 * It is passed a Temporal.PlainDateTime instance, as well as the instance's
364 * calendar object (so that it doesn't have to call the calendar getter itself
365 * if it wants to make any assertions about the calendar.)
367 checkPlainDateTimeConversionFastPath(func, message = "checkPlainDateTimeConversionFastPath") {
371 const calendar = new Temporal.Calendar("iso8601");
372 const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
373 const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDateTime.prototype);
374 ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => {
375 Object.defineProperty(datetime, property, {
377 actual.push(`get ${formatPropertyName(property)}`);
378 const value = prototypeDescrs[property].get.call(this);
381 actual.push(`toString ${formatPropertyName(property)}`);
382 return value.toString();
385 actual.push(`valueOf ${formatPropertyName(property)}`);
392 Object.defineProperty(datetime, "calendar", {
394 actual.push("get calendar");
399 func(datetime, calendar);
400 assert.compareArray(actual, expected, `${message}: property getters not called`);
404 * Check that an options bag that accepts units written in the singular form,
405 * also accepts the same units written in the plural form.
406 * func(unit) should call the method with the appropriate options bag
407 * containing unit as a value. This will be called twice for each element of
408 * validSingularUnits, once with singular and once with plural, and the
409 * results of each pair should be the same (whether a Temporal object or a
412 checkPluralUnitsAccepted(func, validSingularUnits) {
421 millisecond: 'milliseconds',
422 microsecond: 'microseconds',
423 nanosecond: 'nanoseconds',
426 validSingularUnits.forEach((unit) => {
427 const singularValue = func(unit);
428 const pluralValue = func(plurals[unit]);
429 const desc = `Plural ${plurals[unit]} produces the same result as singular ${unit}`;
430 if (singularValue instanceof Temporal.Duration) {
431 TemporalHelpers.assertDurationsEqual(pluralValue, singularValue, desc);
432 } else if (singularValue instanceof Temporal.Instant) {
433 TemporalHelpers.assertInstantsEqual(pluralValue, singularValue, desc);
434 } else if (singularValue instanceof Temporal.PlainDateTime) {
435 TemporalHelpers.assertPlainDateTimesEqual(pluralValue, singularValue, desc);
436 } else if (singularValue instanceof Temporal.PlainTime) {
437 TemporalHelpers.assertPlainTimesEqual(pluralValue, singularValue, desc);
438 } else if (singularValue instanceof Temporal.ZonedDateTime) {
439 TemporalHelpers.assertZonedDateTimesEqual(pluralValue, singularValue, desc);
441 assert.sameValue(pluralValue, singularValue);
447 * checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc):
449 * Checks the type handling of the roundingIncrement option.
450 * checkFunc(roundingIncrement) is a function which takes the value of
451 * roundingIncrement to test, and calls the method under test with it,
452 * returning the result. assertTrueResultFunc(result, description) should
453 * assert that result is the expected result with roundingIncrement: true, and
454 * assertObjectResultFunc(result, description) should assert that result is
455 * the expected result with roundingIncrement being an object with a valueOf()
458 checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc) {
459 // null converts to 0, which is out of range
460 assert.throws(RangeError, () => checkFunc(null), "null");
461 // Booleans convert to either 0 or 1, and 1 is allowed
462 const trueResult = checkFunc(true);
463 assertTrueResultFunc(trueResult, "true");
464 assert.throws(RangeError, () => checkFunc(false), "false");
465 // Symbols and BigInts cannot convert to numbers
466 assert.throws(TypeError, () => checkFunc(Symbol()), "symbol");
467 assert.throws(TypeError, () => checkFunc(2n), "bigint");
469 // Objects prefer their valueOf() methods when converting to a number
470 assert.throws(RangeError, () => checkFunc({}), "plain object");
473 "get roundingIncrement.valueOf",
474 "call roundingIncrement.valueOf",
477 const observer = TemporalHelpers.toPrimitiveObserver(actual, 2, "roundingIncrement");
478 const objectResult = checkFunc(observer);
479 assertObjectResultFunc(objectResult, "object with valueOf");
480 assert.compareArray(actual, expected, "order of operations");
484 * checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc):
486 * Checks the type handling of a string option, of which there are several in
488 * propertyName is the name of the option, and value is the value that
489 * assertFunc should expect it to have.
490 * checkFunc(value) is a function which takes the value of the option to test,
491 * and calls the method under test with it, returning the result.
492 * assertFunc(result, description) should assert that result is the expected
493 * result with the option value being an object with a toString() method
494 * which returns the given value.
496 checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc) {
497 // null converts to the string "null", which is an invalid string value
498 assert.throws(RangeError, () => checkFunc(null), "null");
499 // Booleans convert to the strings "true" or "false", which are invalid
500 assert.throws(RangeError, () => checkFunc(true), "true");
501 assert.throws(RangeError, () => checkFunc(false), "false");
502 // Symbols cannot convert to strings
503 assert.throws(TypeError, () => checkFunc(Symbol()), "symbol");
504 // Numbers convert to strings which are invalid
505 assert.throws(RangeError, () => checkFunc(2), "number");
506 // BigInts convert to strings which are invalid
507 assert.throws(RangeError, () => checkFunc(2n), "bigint");
509 // Objects prefer their toString() methods when converting to a string
510 assert.throws(RangeError, () => checkFunc({}), "plain object");
513 `get ${propertyName}.toString`,
514 `call ${propertyName}.toString`,
517 const observer = TemporalHelpers.toPrimitiveObserver(actual, value, propertyName);
518 const result = checkFunc(observer);
519 assertFunc(result, "object with toString");
520 assert.compareArray(actual, expected, "order of operations");
524 * checkSubclassingIgnored(construct, constructArgs, method, methodArgs,
527 * Methods of Temporal classes that return a new instance of the same class,
528 * must not take the constructor of a subclass into account, nor the @@species
529 * property. This helper runs tests to ensure this.
531 * construct(...constructArgs) must yield a valid instance of the Temporal
532 * class. instance[method](...methodArgs) is the method call under test, which
533 * must also yield a valid instance of the same Temporal class, not a
534 * subclass. See below for the individual tests that this runs.
535 * resultAssertions() is a function that performs additional assertions on the
536 * instance returned by the method under test.
538 checkSubclassingIgnored(...args) {
539 this.checkSubclassConstructorNotObject(...args);
540 this.checkSubclassConstructorUndefined(...args);
541 this.checkSubclassConstructorThrows(...args);
542 this.checkSubclassConstructorNotCalled(...args);
543 this.checkSubclassSpeciesInvalidResult(...args);
544 this.checkSubclassSpeciesNotAConstructor(...args);
545 this.checkSubclassSpeciesNull(...args);
546 this.checkSubclassSpeciesUndefined(...args);
547 this.checkSubclassSpeciesThrows(...args);
551 * Checks that replacing the 'constructor' property of the instance with
552 * various primitive values does not affect the returned new instance.
554 checkSubclassConstructorNotObject(construct, constructArgs, method, methodArgs, resultAssertions) {
555 function check(value, description) {
556 const instance = new construct(...constructArgs);
557 instance.constructor = value;
558 const result = instance[method](...methodArgs);
559 assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
560 resultAssertions(result);
565 check("test", "string");
566 check(Symbol(), "Symbol");
572 * Checks that replacing the 'constructor' property of the subclass with
573 * undefined does not affect the returned new instance.
575 checkSubclassConstructorUndefined(construct, constructArgs, method, methodArgs, resultAssertions) {
578 class MySubclass extends construct {
581 super(...constructArgs);
585 const instance = new MySubclass();
586 assert.sameValue(called, 1);
588 MySubclass.prototype.constructor = undefined;
590 const result = instance[method](...methodArgs);
591 assert.sameValue(called, 1);
592 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
593 resultAssertions(result);
597 * Checks that making the 'constructor' property of the instance throw when
598 * called does not affect the returned new instance.
600 checkSubclassConstructorThrows(construct, constructArgs, method, methodArgs, resultAssertions) {
601 function CustomError() {}
602 const instance = new construct(...constructArgs);
603 Object.defineProperty(instance, "constructor", {
605 throw new CustomError();
608 const result = instance[method](...methodArgs);
609 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
610 resultAssertions(result);
614 * Checks that when subclassing, the subclass constructor is not called by
615 * the method under test.
617 checkSubclassConstructorNotCalled(construct, constructArgs, method, methodArgs, resultAssertions) {
620 class MySubclass extends construct {
623 super(...constructArgs);
627 const instance = new MySubclass();
628 assert.sameValue(called, 1);
630 const result = instance[method](...methodArgs);
631 assert.sameValue(called, 1);
632 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
633 resultAssertions(result);
637 * Check that the constructor's @@species property is ignored when it's a
638 * constructor that returns a non-object value.
640 checkSubclassSpeciesInvalidResult(construct, constructArgs, method, methodArgs, resultAssertions) {
641 function check(value, description) {
642 const instance = new construct(...constructArgs);
643 instance.constructor = {
644 [Symbol.species]: function() {
648 const result = instance[method](...methodArgs);
649 assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
650 resultAssertions(result);
653 check(undefined, "undefined");
656 check("test", "string");
657 check(Symbol(), "Symbol");
660 check({}, "plain object");
664 * Check that the constructor's @@species property is ignored when it's not a
667 checkSubclassSpeciesNotAConstructor(construct, constructArgs, method, methodArgs, resultAssertions) {
668 function check(value, description) {
669 const instance = new construct(...constructArgs);
670 instance.constructor = {
671 [Symbol.species]: value,
673 const result = instance[method](...methodArgs);
674 assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
675 resultAssertions(result);
679 check("test", "string");
680 check(Symbol(), "Symbol");
683 check({}, "plain object");
687 * Check that the constructor's @@species property is ignored when it's null.
689 checkSubclassSpeciesNull(construct, constructArgs, method, methodArgs, resultAssertions) {
692 class MySubclass extends construct {
695 super(...constructArgs);
699 const instance = new MySubclass();
700 assert.sameValue(called, 1);
702 MySubclass.prototype.constructor = {
703 [Symbol.species]: null,
706 const result = instance[method](...methodArgs);
707 assert.sameValue(called, 1);
708 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
709 resultAssertions(result);
713 * Check that the constructor's @@species property is ignored when it's
716 checkSubclassSpeciesUndefined(construct, constructArgs, method, methodArgs, resultAssertions) {
719 class MySubclass extends construct {
722 super(...constructArgs);
726 const instance = new MySubclass();
727 assert.sameValue(called, 1);
729 MySubclass.prototype.constructor = {
730 [Symbol.species]: undefined,
733 const result = instance[method](...methodArgs);
734 assert.sameValue(called, 1);
735 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
736 resultAssertions(result);
740 * Check that the constructor's @@species property is ignored when it throws,
741 * i.e. it is not called at all.
743 checkSubclassSpeciesThrows(construct, constructArgs, method, methodArgs, resultAssertions) {
744 function CustomError() {}
746 const instance = new construct(...constructArgs);
747 instance.constructor = {
748 get [Symbol.species]() {
749 throw new CustomError();
753 const result = instance[method](...methodArgs);
754 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
758 * checkSubclassingIgnoredStatic(construct, method, methodArgs, resultAssertions):
760 * Static methods of Temporal classes that return a new instance of the class,
761 * must not use the this-value as a constructor. This helper runs tests to
764 * construct[method](...methodArgs) is the static method call under test, and
765 * must yield a valid instance of the Temporal class, not a subclass. See
766 * below for the individual tests that this runs.
767 * resultAssertions() is a function that performs additional assertions on the
768 * instance returned by the method under test.
770 checkSubclassingIgnoredStatic(...args) {
771 this.checkStaticInvalidReceiver(...args);
772 this.checkStaticReceiverNotCalled(...args);
773 this.checkThisValueNotCalled(...args);
777 * Check that calling the static method with a receiver that's not callable,
778 * still calls the intrinsic constructor.
780 checkStaticInvalidReceiver(construct, method, methodArgs, resultAssertions) {
781 function check(value, description) {
782 const result = construct[method].apply(value, methodArgs);
783 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
784 resultAssertions(result);
787 check(undefined, "undefined");
790 check("test", "string");
791 check(Symbol(), "symbol");
794 check({}, "Non-callable object");
798 * Check that calling the static method with a receiver that returns a value
799 * that's not callable, still calls the intrinsic constructor.
801 checkStaticReceiverNotCalled(construct, method, methodArgs, resultAssertions) {
802 function check(value, description) {
803 const receiver = function () {
806 const result = construct[method].apply(receiver, methodArgs);
807 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
808 resultAssertions(result);
811 check(undefined, "undefined");
814 check("test", "string");
815 check(Symbol(), "symbol");
818 check({}, "Non-callable object");
822 * Check that the receiver isn't called.
824 checkThisValueNotCalled(construct, method, methodArgs, resultAssertions) {
827 class MySubclass extends construct {
828 constructor(...args) {
834 const result = MySubclass[method](...methodArgs);
835 assert.sameValue(called, false);
836 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
837 resultAssertions(result);
841 * Check that any iterable returned from a custom time zone's
842 * getPossibleInstantsFor() method is exhausted.
843 * The custom time zone object is passed in to func().
844 * expected is an array of strings representing the expected calls to the
845 * getPossibleInstantsFor() method. The PlainDateTimes that it is called with,
846 * are compared (using their toString() results) with the array.
848 checkTimeZonePossibleInstantsIterable(func, expected) {
849 // A custom time zone that returns an iterable instead of an array from its
850 // getPossibleInstantsFor() method, and for testing purposes skips
851 // 00:00-01:00 UTC on January 1, 2030, and repeats 00:00-01:00 UTC+1 on
852 // January 3, 2030. Otherwise identical to the UTC time zone.
853 class TimeZonePossibleInstantsIterable extends Temporal.TimeZone {
856 this.getPossibleInstantsForCallCount = 0;
857 this.getPossibleInstantsForCalledWith = [];
858 this.getPossibleInstantsForReturns = [];
859 this.iteratorExhausted = [];
863 return "Custom/Iterable";
866 getOffsetNanosecondsFor(instant) {
867 if (Temporal.Instant.compare(instant, "2030-01-01T00:00Z") >= 0 &&
868 Temporal.Instant.compare(instant, "2030-01-03T01:00Z") < 0) {
869 return 3600_000_000_000;
875 getPossibleInstantsFor(dateTime) {
876 this.getPossibleInstantsForCallCount++;
877 this.getPossibleInstantsForCalledWith.push(dateTime);
879 // Fake DST transition
880 let retval = super.getPossibleInstantsFor(dateTime);
881 if (dateTime.toPlainDate().equals("2030-01-01") && dateTime.hour === 0) {
883 } else if (dateTime.toPlainDate().equals("2030-01-03") && dateTime.hour === 0) {
884 retval.push(retval[0].subtract({ hours: 1 }));
885 } else if (dateTime.year === 2030 && dateTime.month === 1 && dateTime.day >= 1 && dateTime.day <= 2) {
886 retval[0] = retval[0].subtract({ hours: 1 });
889 this.getPossibleInstantsForReturns.push(retval);
890 this.iteratorExhausted.push(false);
892 callIndex: this.getPossibleInstantsForCallCount - 1,
894 *[Symbol.iterator]() {
895 yield* this.timeZone.getPossibleInstantsForReturns[this.callIndex];
896 this.timeZone.iteratorExhausted[this.callIndex] = true;
902 const timeZone = new TimeZonePossibleInstantsIterable();
905 assert.sameValue(timeZone.getPossibleInstantsForCallCount, expected.length, "getPossibleInstantsFor() method called correct number of times");
907 for (let index = 0; index < expected.length; index++) {
908 assert.sameValue(timeZone.getPossibleInstantsForCalledWith[index].toString(), expected[index], "getPossibleInstantsFor() called with expected PlainDateTime");
909 assert(timeZone.iteratorExhausted[index], "iterated through the whole iterable");
914 * Check that any calendar-carrying Temporal object has its [[Calendar]]
915 * internal slot read by ToTemporalCalendar, and does not fetch the calendar
916 * by calling getters.
917 * The custom calendar object is passed in to func() so that it can do its
918 * own additional assertions involving the calendar if necessary. (Sometimes
919 * there is nothing to assert as the calendar isn't stored anywhere that can
920 * be asserted about.)
922 checkToTemporalCalendarFastPath(func) {
923 class CalendarFastPathCheck extends Temporal.Calendar {
928 dateFromFields(...args) {
929 return super.dateFromFields(...args).withCalendar(this);
932 monthDayFromFields(...args) {
933 const { isoYear, isoMonth, isoDay } = super.monthDayFromFields(...args).getISOFields();
934 return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
937 yearMonthFromFields(...args) {
938 const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields(...args).getISOFields();
939 return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
943 return "fast-path-check";
946 const calendar = new CalendarFastPathCheck();
948 const plainDate = new Temporal.PlainDate(2000, 5, 2, calendar);
949 const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
950 const plainMonthDay = new Temporal.PlainMonthDay(5, 2, calendar);
951 const plainYearMonth = new Temporal.PlainYearMonth(2000, 5, calendar);
952 const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
954 [plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((temporalObject) => {
958 Object.defineProperty(temporalObject, "calendar", {
960 actual.push("get calendar");
965 func(temporalObject, calendar);
966 assert.compareArray(actual, expected, "calendar getter not called");
970 checkToTemporalInstantFastPath(func) {
974 const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
975 Object.defineProperty(datetime, 'toString', {
977 actual.push("get toString");
978 return function (options) {
979 actual.push("call toString");
980 return Temporal.ZonedDateTime.prototype.toString.call(this, options);
986 assert.compareArray(actual, expected, "toString not called");
989 checkToTemporalPlainDateTimeFastPath(func) {
993 const calendar = new Temporal.Calendar("iso8601");
994 const date = new Temporal.PlainDate(2000, 5, 2, calendar);
995 const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDate.prototype);
996 ["year", "month", "monthCode", "day"].forEach((property) => {
997 Object.defineProperty(date, property, {
999 actual.push(`get ${formatPropertyName(property)}`);
1000 const value = prototypeDescrs[property].get.call(this);
1001 return TemporalHelpers.toPrimitiveObserver(actual, value, property);
1005 ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => {
1006 Object.defineProperty(date, property, {
1008 actual.push(`get ${formatPropertyName(property)}`);
1013 Object.defineProperty(date, "calendar", {
1015 actual.push("get calendar");
1020 func(date, calendar);
1021 assert.compareArray(actual, expected, "property getters not called");
1025 * A custom calendar used in prototype pollution checks. Verifies that the
1026 * fromFields methods are always called with a null-prototype fields object.
1028 calendarCheckFieldsPrototypePollution() {
1029 class CalendarCheckFieldsPrototypePollution extends Temporal.Calendar {
1032 this.dateFromFieldsCallCount = 0;
1033 this.yearMonthFromFieldsCallCount = 0;
1034 this.monthDayFromFieldsCallCount = 0;
1037 // toString must remain "iso8601", so that some methods don't throw due to
1038 // incompatible calendars
1040 dateFromFields(fields, options = {}) {
1041 this.dateFromFieldsCallCount++;
1042 assert.sameValue(Object.getPrototypeOf(fields), null, "dateFromFields should be called with null-prototype fields object");
1043 return super.dateFromFields(fields, options);
1046 yearMonthFromFields(fields, options = {}) {
1047 this.yearMonthFromFieldsCallCount++;
1048 assert.sameValue(Object.getPrototypeOf(fields), null, "yearMonthFromFields should be called with null-prototype fields object");
1049 return super.yearMonthFromFields(fields, options);
1052 monthDayFromFields(fields, options = {}) {
1053 this.monthDayFromFieldsCallCount++;
1054 assert.sameValue(Object.getPrototypeOf(fields), null, "monthDayFromFields should be called with null-prototype fields object");
1055 return super.monthDayFromFields(fields, options);
1059 return new CalendarCheckFieldsPrototypePollution();
1063 * A custom calendar used in prototype pollution checks. Verifies that the
1064 * mergeFields() method is always called with null-prototype fields objects.
1066 calendarCheckMergeFieldsPrototypePollution() {
1067 class CalendarCheckMergeFieldsPrototypePollution extends Temporal.Calendar {
1070 this.mergeFieldsCallCount = 0;
1074 return "merge-fields-null-proto";
1077 mergeFields(fields, additionalFields) {
1078 this.mergeFieldsCallCount++;
1079 assert.sameValue(Object.getPrototypeOf(fields), null, "mergeFields should be called with null-prototype fields object (first argument)");
1080 assert.sameValue(Object.getPrototypeOf(additionalFields), null, "mergeFields should be called with null-prototype fields object (second argument)");
1081 return super.mergeFields(fields, additionalFields);
1085 return new CalendarCheckMergeFieldsPrototypePollution();
1089 * A custom calendar used in prototype pollution checks. Verifies that methods
1090 * are always called with a null-prototype options object.
1092 calendarCheckOptionsPrototypePollution() {
1093 class CalendarCheckOptionsPrototypePollution extends Temporal.Calendar {
1096 this.yearMonthFromFieldsCallCount = 0;
1097 this.dateUntilCallCount = 0;
1101 return "options-null-proto";
1104 yearMonthFromFields(fields, options) {
1105 this.yearMonthFromFieldsCallCount++;
1106 assert.sameValue(Object.getPrototypeOf(options), null, "yearMonthFromFields should be called with null-prototype options");
1107 return super.yearMonthFromFields(fields, options);
1110 dateUntil(one, two, options) {
1111 this.dateUntilCallCount++;
1112 assert.sameValue(Object.getPrototypeOf(options), null, "dateUntil should be called with null-prototype options");
1113 return super.dateUntil(one, two, options);
1117 return new CalendarCheckOptionsPrototypePollution();
1121 * A custom calendar that asserts its dateAdd() method is called with the
1122 * options parameter having the value undefined.
1124 calendarDateAddUndefinedOptions() {
1125 class CalendarDateAddUndefinedOptions extends Temporal.Calendar {
1128 this.dateAddCallCount = 0;
1132 return "dateadd-undef-options";
1135 dateAdd(date, duration, options) {
1136 this.dateAddCallCount++;
1137 assert.sameValue(options, undefined, "dateAdd shouldn't be called with options");
1138 return super.dateAdd(date, duration, options);
1141 return new CalendarDateAddUndefinedOptions();
1145 * A custom calendar that asserts its dateAdd() method is called with a
1146 * PlainDate instance. Optionally, it also asserts that the PlainDate instance
1147 * is the specific object `this.specificPlainDate`, if it is set by the
1150 calendarDateAddPlainDateInstance() {
1151 class CalendarDateAddPlainDateInstance extends Temporal.Calendar {
1154 this.dateAddCallCount = 0;
1155 this.specificPlainDate = undefined;
1159 return "dateadd-plain-date-instance";
1162 dateFromFields(...args) {
1163 return super.dateFromFields(...args).withCalendar(this);
1166 dateAdd(date, duration, options) {
1167 this.dateAddCallCount++;
1168 assert(date instanceof Temporal.PlainDate, "dateAdd() should be called with a PlainDate instance");
1169 if (this.dateAddCallCount === 1 && this.specificPlainDate) {
1170 assert.sameValue(date, this.specificPlainDate, `dateAdd() should be called first with the specific PlainDate instance ${this.specificPlainDate}`);
1172 return super.dateAdd(date, duration, options).withCalendar(this);
1175 return new CalendarDateAddPlainDateInstance();
1179 * A custom calendar that returns an iterable instead of an array from its
1180 * fields() method, otherwise identical to the ISO calendar.
1182 calendarFieldsIterable() {
1183 class CalendarFieldsIterable extends Temporal.Calendar {
1186 this.fieldsCallCount = 0;
1187 this.fieldsCalledWith = [];
1188 this.iteratorExhausted = [];
1192 return "fields-iterable";
1195 fields(fieldNames) {
1196 this.fieldsCallCount++;
1197 this.fieldsCalledWith.push(fieldNames.slice());
1198 this.iteratorExhausted.push(false);
1200 callIndex: this.fieldsCallCount - 1,
1202 *[Symbol.iterator]() {
1203 yield* this.calendar.fieldsCalledWith[this.callIndex];
1204 this.calendar.iteratorExhausted[this.callIndex] = true;
1209 return new CalendarFieldsIterable();
1213 * A custom calendar that asserts its ...FromFields() methods are called with
1214 * the options parameter having the value undefined.
1216 calendarFromFieldsUndefinedOptions() {
1217 class CalendarFromFieldsUndefinedOptions extends Temporal.Calendar {
1220 this.dateFromFieldsCallCount = 0;
1221 this.monthDayFromFieldsCallCount = 0;
1222 this.yearMonthFromFieldsCallCount = 0;
1226 return "from-fields-undef-options";
1229 dateFromFields(fields, options) {
1230 this.dateFromFieldsCallCount++;
1231 assert.sameValue(options, undefined, "dateFromFields shouldn't be called with options");
1232 return super.dateFromFields(fields, options);
1235 yearMonthFromFields(fields, options) {
1236 this.yearMonthFromFieldsCallCount++;
1237 assert.sameValue(options, undefined, "yearMonthFromFields shouldn't be called with options");
1238 return super.yearMonthFromFields(fields, options);
1241 monthDayFromFields(fields, options) {
1242 this.monthDayFromFieldsCallCount++;
1243 assert.sameValue(options, undefined, "monthDayFromFields shouldn't be called with options");
1244 return super.monthDayFromFields(fields, options);
1247 return new CalendarFromFieldsUndefinedOptions();
1251 * A custom calendar that modifies the fields object passed in to
1252 * dateFromFields, sabotaging its time properties.
1254 calendarMakeInfinityTime() {
1255 class CalendarMakeInfinityTime extends Temporal.Calendar {
1260 dateFromFields(fields, options) {
1261 const retval = super.dateFromFields(fields, options);
1262 fields.hour = Infinity;
1263 fields.minute = Infinity;
1264 fields.second = Infinity;
1265 fields.millisecond = Infinity;
1266 fields.microsecond = Infinity;
1267 fields.nanosecond = Infinity;
1271 return new CalendarMakeInfinityTime();
1275 * A custom calendar that defines getters on the fields object passed into
1276 * dateFromFields that throw, sabotaging its time properties.
1278 calendarMakeInvalidGettersTime() {
1279 class CalendarMakeInvalidGettersTime extends Temporal.Calendar {
1284 dateFromFields(fields, options) {
1285 const retval = super.dateFromFields(fields, options);
1286 const throwingDescriptor = {
1288 throw new Test262Error("reading a sabotaged time field");
1291 Object.defineProperties(fields, {
1292 hour: throwingDescriptor,
1293 minute: throwingDescriptor,
1294 second: throwingDescriptor,
1295 millisecond: throwingDescriptor,
1296 microsecond: throwingDescriptor,
1297 nanosecond: throwingDescriptor,
1302 return new CalendarMakeInvalidGettersTime();
1306 * A custom calendar whose mergeFields() method returns a proxy object with
1307 * all of its Get and HasProperty operations observable, as well as adding a
1308 * "shouldNotBeCopied": true property.
1310 calendarMergeFieldsGetters() {
1311 class CalendarMergeFieldsGetters extends Temporal.Calendar {
1314 this.mergeFieldsReturnOperations = [];
1318 return "merge-fields-getters";
1321 dateFromFields(fields, options) {
1322 assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
1323 return super.dateFromFields(fields, options);
1326 yearMonthFromFields(fields, options) {
1327 assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
1328 return super.yearMonthFromFields(fields, options);
1331 monthDayFromFields(fields, options) {
1332 assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
1333 return super.monthDayFromFields(fields, options);
1336 mergeFields(fields, additionalFields) {
1337 const retval = super.mergeFields(fields, additionalFields);
1338 retval._calendar = this;
1339 retval.shouldNotBeCopied = true;
1340 return new Proxy(retval, {
1342 target._calendar.mergeFieldsReturnOperations.push(`get ${key}`);
1343 const result = target[key];
1344 if (result === undefined) {
1347 return TemporalHelpers.toPrimitiveObserver(target._calendar.mergeFieldsReturnOperations, result, key);
1350 target._calendar.mergeFieldsReturnOperations.push(`has ${key}`);
1351 return key in target;
1356 return new CalendarMergeFieldsGetters();
1360 * A custom calendar whose mergeFields() method returns a primitive value,
1361 * given by @primitive, and which records the number of calls made to its
1362 * dateFromFields(), yearMonthFromFields(), and monthDayFromFields() methods.
1364 calendarMergeFieldsReturnsPrimitive(primitive) {
1365 class CalendarMergeFieldsPrimitive extends Temporal.Calendar {
1366 constructor(mergeFieldsReturnValue) {
1368 this._mergeFieldsReturnValue = mergeFieldsReturnValue;
1369 this.dateFromFieldsCallCount = 0;
1370 this.monthDayFromFieldsCallCount = 0;
1371 this.yearMonthFromFieldsCallCount = 0;
1375 return "merge-fields-primitive";
1378 dateFromFields(fields, options) {
1379 this.dateFromFieldsCallCount++;
1380 return super.dateFromFields(fields, options);
1383 yearMonthFromFields(fields, options) {
1384 this.yearMonthFromFieldsCallCount++;
1385 return super.yearMonthFromFields(fields, options);
1388 monthDayFromFields(fields, options) {
1389 this.monthDayFromFieldsCallCount++;
1390 return super.monthDayFromFields(fields, options);
1394 return this._mergeFieldsReturnValue;
1397 return new CalendarMergeFieldsPrimitive(primitive);
1401 * A custom calendar whose fields() method returns the same value as the
1402 * iso8601 calendar, with the addition of extraFields provided as parameter.
1404 calendarWithExtraFields(fields) {
1405 class CalendarWithExtraFields extends Temporal.Calendar {
1406 constructor(extraFields) {
1408 this._extraFields = extraFields;
1411 fields(fieldNames) {
1412 return super.fields(fieldNames).concat(this._extraFields);
1416 return new CalendarWithExtraFields(fields);
1420 * crossDateLineTimeZone():
1422 * This returns an instance of a custom time zone class that implements one
1423 * single transition where the time zone moves from one side of the
1424 * International Date Line to the other, for the purpose of testing time zone
1425 * calculations without depending on system time zone data.
1427 * The transition occurs at epoch second 1325239200 and goes from offset
1428 * -10:00 to +14:00. In other words, the time zone skips the whole calendar
1429 * day of 2011-12-30. This is the same as the real-life transition in the
1430 * Pacific/Apia time zone.
1432 crossDateLineTimeZone() {
1433 const { compare } = Temporal.PlainDate;
1434 const skippedDay = new Temporal.PlainDate(2011, 12, 30);
1435 const transitionEpoch = 1325239200_000_000_000n;
1436 const beforeOffset = new Temporal.TimeZone("-10:00");
1437 const afterOffset = new Temporal.TimeZone("+14:00");
1439 class CrossDateLineTimeZone extends Temporal.TimeZone {
1444 getOffsetNanosecondsFor(instant) {
1445 if (instant.epochNanoseconds < transitionEpoch) {
1446 return beforeOffset.getOffsetNanosecondsFor(instant);
1448 return afterOffset.getOffsetNanosecondsFor(instant);
1451 getPossibleInstantsFor(datetime) {
1452 const comparison = compare(datetime.toPlainDate(), skippedDay);
1453 if (comparison === 0) {
1456 if (comparison < 0) {
1457 return [beforeOffset.getInstantFor(datetime)];
1459 return [afterOffset.getInstantFor(datetime)];
1462 getPreviousTransition(instant) {
1463 if (instant.epochNanoseconds > transitionEpoch) return new Temporal.Instant(transitionEpoch);
1467 getNextTransition(instant) {
1468 if (instant.epochNanoseconds < transitionEpoch) return new Temporal.Instant(transitionEpoch);
1473 return "Custom/Date_Line";
1476 return new CrossDateLineTimeZone();
1480 * observeProperty(calls, object, propertyName, value):
1482 * Defines an own property @object.@propertyName with value @value, that
1483 * will log any calls to its accessors to the array @calls.
1485 observeProperty(calls, object, propertyName, value, objectName = "") {
1486 Object.defineProperty(object, propertyName, {
1488 calls.push(`get ${formatPropertyName(propertyName, objectName)}`);
1492 calls.push(`set ${formatPropertyName(propertyName, objectName)}`);
1498 * observeMethod(calls, object, propertyName, value):
1500 * Defines an own property @object.@propertyName with value @value, that
1501 * will log any calls of @value to the array @calls.
1503 observeMethod(calls, object, propertyName, objectName = "") {
1504 const method = object[propertyName];
1505 object[propertyName] = function () {
1506 calls.push(`call ${formatPropertyName(propertyName, objectName)}`);
1507 return method.apply(object, arguments);
1512 * Used for substituteMethod to indicate default behavior instead of a
1515 SUBSTITUTE_SKIP: SKIP_SYMBOL,
1518 * substituteMethod(object, propertyName, values):
1520 * Defines an own property @object.@propertyName that will, for each
1521 * subsequent call to the method previously defined as
1522 * @object.@propertyName:
1523 * - Call the method, if no more values remain
1524 * - Call the method, if the value in @values for the corresponding call
1525 * is SUBSTITUTE_SKIP
1526 * - Otherwise, return the corresponding value in @value
1528 substituteMethod(object, propertyName, values) {
1530 const method = object[propertyName];
1531 object[propertyName] = function () {
1532 if (calls >= values.length) {
1533 return method.apply(object, arguments);
1534 } else if (values[calls] === SKIP_SYMBOL) {
1536 return method.apply(object, arguments);
1538 return values[calls++];
1545 * A custom calendar that behaves exactly like the ISO 8601 calendar but
1546 * tracks calls to any of its methods, and Get/Has operations on its
1547 * properties, by appending messages to an array. This is for the purpose of
1548 * testing order of operations that are observable from user code.
1549 * objectName is used in the log.
1551 calendarObserver(calls, objectName, methodOverrides = {}) {
1552 function removeExtraHasPropertyChecks(objectName, calls) {
1553 // Inserting the tracking calendar into the return values of methods
1554 // that we chain up into the ISO calendar for, causes extra HasProperty
1555 // checks, which we observe. This removes them so that we don't leak
1556 // implementation details of the helper into the test code.
1557 assert.sameValue(calls.pop(), `has ${objectName}.yearOfWeek`);
1558 assert.sameValue(calls.pop(), `has ${objectName}.yearMonthFromFields`);
1559 assert.sameValue(calls.pop(), `has ${objectName}.year`);
1560 assert.sameValue(calls.pop(), `has ${objectName}.weekOfYear`);
1561 assert.sameValue(calls.pop(), `has ${objectName}.monthsInYear`);
1562 assert.sameValue(calls.pop(), `has ${objectName}.monthDayFromFields`);
1563 assert.sameValue(calls.pop(), `has ${objectName}.monthCode`);
1564 assert.sameValue(calls.pop(), `has ${objectName}.month`);
1565 assert.sameValue(calls.pop(), `has ${objectName}.mergeFields`);
1566 assert.sameValue(calls.pop(), `has ${objectName}.inLeapYear`);
1567 assert.sameValue(calls.pop(), `has ${objectName}.id`);
1568 assert.sameValue(calls.pop(), `has ${objectName}.fields`);
1569 assert.sameValue(calls.pop(), `has ${objectName}.daysInYear`);
1570 assert.sameValue(calls.pop(), `has ${objectName}.daysInWeek`);
1571 assert.sameValue(calls.pop(), `has ${objectName}.daysInMonth`);
1572 assert.sameValue(calls.pop(), `has ${objectName}.dayOfYear`);
1573 assert.sameValue(calls.pop(), `has ${objectName}.dayOfWeek`);
1574 assert.sameValue(calls.pop(), `has ${objectName}.day`);
1575 assert.sameValue(calls.pop(), `has ${objectName}.dateUntil`);
1576 assert.sameValue(calls.pop(), `has ${objectName}.dateFromFields`);
1577 assert.sameValue(calls.pop(), `has ${objectName}.dateAdd`);
1580 const iso8601 = new Temporal.Calendar("iso8601");
1581 const trackingMethods = {
1582 dateFromFields(...args) {
1583 calls.push(`call ${objectName}.dateFromFields`);
1584 if ('dateFromFields' in methodOverrides) {
1585 const value = methodOverrides.dateFromFields;
1586 return typeof value === "function" ? value(...args) : value;
1588 const originalResult = iso8601.dateFromFields(...args);
1589 // Replace the calendar in the result with the call-tracking calendar
1590 const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
1591 const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this);
1592 removeExtraHasPropertyChecks(objectName, calls);
1595 yearMonthFromFields(...args) {
1596 calls.push(`call ${objectName}.yearMonthFromFields`);
1597 if ('yearMonthFromFields' in methodOverrides) {
1598 const value = methodOverrides.yearMonthFromFields;
1599 return typeof value === "function" ? value(...args) : value;
1601 const originalResult = iso8601.yearMonthFromFields(...args);
1602 // Replace the calendar in the result with the call-tracking calendar
1603 const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
1604 const result = new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
1605 removeExtraHasPropertyChecks(objectName, calls);
1608 monthDayFromFields(...args) {
1609 calls.push(`call ${objectName}.monthDayFromFields`);
1610 if ('monthDayFromFields' in methodOverrides) {
1611 const value = methodOverrides.monthDayFromFields;
1612 return typeof value === "function" ? value(...args) : value;
1614 const originalResult = iso8601.monthDayFromFields(...args);
1615 // Replace the calendar in the result with the call-tracking calendar
1616 const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
1617 const result = new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
1618 removeExtraHasPropertyChecks(objectName, calls);
1622 calls.push(`call ${objectName}.dateAdd`);
1623 if ('dateAdd' in methodOverrides) {
1624 const value = methodOverrides.dateAdd;
1625 return typeof value === "function" ? value(...args) : value;
1627 const originalResult = iso8601.dateAdd(...args);
1628 const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
1629 const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this);
1630 removeExtraHasPropertyChecks(objectName, calls);
1635 // Automatically generate the other methods that don't need any custom code
1656 ].forEach((methodName) => {
1657 trackingMethods[methodName] = function (...args) {
1658 calls.push(`call ${formatPropertyName(methodName, objectName)}`);
1659 if (methodName in methodOverrides) {
1660 const value = methodOverrides[methodName];
1661 return typeof value === "function" ? value(...args) : value;
1663 return iso8601[methodName](...args);
1666 return new Proxy(trackingMethods, {
1667 get(target, key, receiver) {
1668 const result = Reflect.get(target, key, receiver);
1669 calls.push(`get ${formatPropertyName(key, objectName)}`);
1673 calls.push(`has ${formatPropertyName(key, objectName)}`);
1674 return Reflect.has(target, key);
1680 * A custom calendar that does not allow any of its methods to be called, for
1681 * the purpose of asserting that a particular operation does not call into
1684 calendarThrowEverything() {
1685 class CalendarThrowEverything extends Temporal.Calendar {
1690 TemporalHelpers.assertUnreachable("toString should not be called");
1693 TemporalHelpers.assertUnreachable("dateFromFields should not be called");
1695 yearMonthFromFields() {
1696 TemporalHelpers.assertUnreachable("yearMonthFromFields should not be called");
1698 monthDayFromFields() {
1699 TemporalHelpers.assertUnreachable("monthDayFromFields should not be called");
1702 TemporalHelpers.assertUnreachable("dateAdd should not be called");
1705 TemporalHelpers.assertUnreachable("dateUntil should not be called");
1708 TemporalHelpers.assertUnreachable("era should not be called");
1711 TemporalHelpers.assertUnreachable("eraYear should not be called");
1714 TemporalHelpers.assertUnreachable("year should not be called");
1717 TemporalHelpers.assertUnreachable("month should not be called");
1720 TemporalHelpers.assertUnreachable("monthCode should not be called");
1723 TemporalHelpers.assertUnreachable("day should not be called");
1726 TemporalHelpers.assertUnreachable("fields should not be called");
1729 TemporalHelpers.assertUnreachable("mergeFields should not be called");
1733 return new CalendarThrowEverything();
1737 * oneShiftTimeZone(shiftInstant, shiftNanoseconds):
1739 * In the case of a spring-forward time zone offset transition (skipped time),
1740 * and disambiguation === 'earlier', BuiltinTimeZoneGetInstantFor subtracts a
1741 * negative number of nanoseconds from a PlainDateTime, which should balance
1742 * with the microseconds field.
1744 * This returns an instance of a custom time zone class which skips a length
1745 * of time equal to shiftNanoseconds (a number), at the Temporal.Instant
1746 * shiftInstant. Before shiftInstant, it's identical to UTC, and after
1747 * shiftInstant it's a constant-offset time zone.
1749 * It provides a getPossibleInstantsForCalledWith member which is an array
1750 * with the result of calling toString() on any PlainDateTimes passed to
1751 * getPossibleInstantsFor().
1753 oneShiftTimeZone(shiftInstant, shiftNanoseconds) {
1754 class OneShiftTimeZone extends Temporal.TimeZone {
1755 constructor(shiftInstant, shiftNanoseconds) {
1757 this._shiftInstant = shiftInstant;
1758 this._epoch1 = shiftInstant.epochNanoseconds;
1759 this._epoch2 = this._epoch1 + BigInt(shiftNanoseconds);
1760 this._shiftNanoseconds = shiftNanoseconds;
1761 this._shift = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, this._shiftNanoseconds);
1762 this.getPossibleInstantsForCalledWith = [];
1765 _isBeforeShift(instant) {
1766 return instant.epochNanoseconds < this._epoch1;
1769 getOffsetNanosecondsFor(instant) {
1770 return this._isBeforeShift(instant) ? 0 : this._shiftNanoseconds;
1773 getPossibleInstantsFor(plainDateTime) {
1774 this.getPossibleInstantsForCalledWith.push(plainDateTime.toString({ calendarName: "never" }));
1775 const [instant] = super.getPossibleInstantsFor(plainDateTime);
1776 if (this._shiftNanoseconds > 0) {
1777 if (this._isBeforeShift(instant)) return [instant];
1778 if (instant.epochNanoseconds < this._epoch2) return [];
1779 return [instant.subtract(this._shift)];
1781 if (instant.epochNanoseconds < this._epoch2) return [instant];
1782 const shifted = instant.subtract(this._shift);
1783 if (this._isBeforeShift(instant)) return [instant, shifted];
1787 getNextTransition(instant) {
1788 return this._isBeforeShift(instant) ? this._shiftInstant : null;
1791 getPreviousTransition(instant) {
1792 return this._isBeforeShift(instant) ? null : this._shiftInstant;
1796 return "Custom/One_Shift";
1799 return new OneShiftTimeZone(shiftInstant, shiftNanoseconds);
1803 * propertyBagObserver():
1804 * Returns an object that behaves like the given propertyBag but tracks Get
1805 * and Has operations on any of its properties, by appending messages to an
1806 * array. If the value of a property in propertyBag is a primitive, the value
1807 * of the returned object's property will additionally be a
1808 * TemporalHelpers.toPrimitiveObserver that will track calls to its toString
1809 * and valueOf methods in the same array. This is for the purpose of testing
1810 * order of operations that are observable from user code. objectName is used
1813 propertyBagObserver(calls, propertyBag, objectName) {
1814 return new Proxy(propertyBag, {
1816 calls.push(`ownKeys ${objectName}`);
1817 return Reflect.ownKeys(target);
1819 getOwnPropertyDescriptor(target, key) {
1820 calls.push(`getOwnPropertyDescriptor ${formatPropertyName(key, objectName)}`);
1821 return Reflect.getOwnPropertyDescriptor(target, key);
1823 get(target, key, receiver) {
1824 calls.push(`get ${formatPropertyName(key, objectName)}`);
1825 const result = Reflect.get(target, key, receiver);
1826 if (result === undefined) {
1829 if ((result !== null && typeof result === "object") || typeof result === "function") {
1832 return TemporalHelpers.toPrimitiveObserver(calls, result, `${formatPropertyName(key, objectName)}`);
1835 calls.push(`has ${formatPropertyName(key, objectName)}`);
1836 return Reflect.has(target, key);
1842 * specificOffsetTimeZone():
1844 * This returns an instance of a custom time zone class, which returns a
1845 * specific custom value from its getOffsetNanosecondsFrom() method. This is
1846 * for the purpose of testing the validation of what this method returns.
1848 * It also returns an empty array from getPossibleInstantsFor(), so as to
1849 * trigger calls to getOffsetNanosecondsFor() when used from the
1850 * BuiltinTimeZoneGetInstantFor operation.
1852 specificOffsetTimeZone(offsetValue) {
1853 class SpecificOffsetTimeZone extends Temporal.TimeZone {
1854 constructor(offsetValue) {
1856 this._offsetValue = offsetValue;
1859 getOffsetNanosecondsFor() {
1860 return this._offsetValue;
1863 getPossibleInstantsFor(dt) {
1864 if (typeof this._offsetValue !== 'number' || Math.abs(this._offsetValue) >= 86400e9 || isNaN(this._offsetValue)) return [];
1865 const zdt = dt.toZonedDateTime("UTC").add({ nanoseconds: -this._offsetValue });
1866 return [zdt.toInstant()];
1870 return this.getOffsetStringFor(new Temporal.Instant(0n));
1873 return new SpecificOffsetTimeZone(offsetValue);
1877 * springForwardFallBackTimeZone():
1879 * This returns an instance of a custom time zone class that implements one
1880 * single spring-forward/fall-back transition, for the purpose of testing the
1881 * disambiguation option, without depending on system time zone data.
1883 * The spring-forward occurs at epoch second 954669600 (2000-04-02T02:00
1884 * local) and goes from offset -08:00 to -07:00.
1886 * The fall-back occurs at epoch second 972810000 (2000-10-29T02:00 local) and
1887 * goes from offset -07:00 to -08:00.
1889 springForwardFallBackTimeZone() {
1890 const { compare } = Temporal.PlainDateTime;
1891 const springForwardLocal = new Temporal.PlainDateTime(2000, 4, 2, 2);
1892 const springForwardEpoch = 954669600_000_000_000n;
1893 const fallBackLocal = new Temporal.PlainDateTime(2000, 10, 29, 1);
1894 const fallBackEpoch = 972810000_000_000_000n;
1895 const winterOffset = new Temporal.TimeZone('-08:00');
1896 const summerOffset = new Temporal.TimeZone('-07:00');
1898 class SpringForwardFallBackTimeZone extends Temporal.TimeZone {
1903 getOffsetNanosecondsFor(instant) {
1904 if (instant.epochNanoseconds < springForwardEpoch ||
1905 instant.epochNanoseconds >= fallBackEpoch) {
1906 return winterOffset.getOffsetNanosecondsFor(instant);
1908 return summerOffset.getOffsetNanosecondsFor(instant);
1911 getPossibleInstantsFor(datetime) {
1912 if (compare(datetime, springForwardLocal) >= 0 && compare(datetime, springForwardLocal.add({ hours: 1 })) < 0) {
1915 if (compare(datetime, fallBackLocal) >= 0 && compare(datetime, fallBackLocal.add({ hours: 1 })) < 0) {
1916 return [summerOffset.getInstantFor(datetime), winterOffset.getInstantFor(datetime)];
1918 if (compare(datetime, springForwardLocal) < 0 || compare(datetime, fallBackLocal) >= 0) {
1919 return [winterOffset.getInstantFor(datetime)];
1921 return [summerOffset.getInstantFor(datetime)];
1924 getPreviousTransition(instant) {
1925 if (instant.epochNanoseconds > fallBackEpoch) return new Temporal.Instant(fallBackEpoch);
1926 if (instant.epochNanoseconds > springForwardEpoch) return new Temporal.Instant(springForwardEpoch);
1930 getNextTransition(instant) {
1931 if (instant.epochNanoseconds < springForwardEpoch) return new Temporal.Instant(springForwardEpoch);
1932 if (instant.epochNanoseconds < fallBackEpoch) return new Temporal.Instant(fallBackEpoch);
1937 return "Custom/Spring_Fall";
1941 return "Custom/Spring_Fall";
1944 return new SpringForwardFallBackTimeZone();
1949 * A custom calendar that behaves exactly like the UTC time zone but tracks
1950 * calls to any of its methods, and Get/Has operations on its properties, by
1951 * appending messages to an array. This is for the purpose of testing order of
1952 * operations that are observable from user code. objectName is used in the
1953 * log. methodOverrides is an optional object containing properties with the
1954 * same name as Temporal.TimeZone methods. If the property value is a function
1955 * it will be called with the proper arguments instead of the UTC method.
1956 * Otherwise, the property value will be returned directly.
1958 timeZoneObserver(calls, objectName, methodOverrides = {}) {
1959 const utc = new Temporal.TimeZone("UTC");
1960 const trackingMethods = {
1963 // Automatically generate the methods
1964 ["getOffsetNanosecondsFor", "getPossibleInstantsFor", "toString"].forEach((methodName) => {
1965 trackingMethods[methodName] = function (...args) {
1966 calls.push(`call ${formatPropertyName(methodName, objectName)}`);
1967 if (methodName in methodOverrides) {
1968 const value = methodOverrides[methodName];
1969 return typeof value === "function" ? value(...args) : value;
1971 return utc[methodName](...args);
1974 return new Proxy(trackingMethods, {
1975 get(target, key, receiver) {
1976 const result = Reflect.get(target, key, receiver);
1977 calls.push(`get ${formatPropertyName(key, objectName)}`);
1981 calls.push(`has ${formatPropertyName(key, objectName)}`);
1982 return Reflect.has(target, key);
1988 * A custom time zone that does not allow any of its methods to be called, for
1989 * the purpose of asserting that a particular operation does not call into
1992 timeZoneThrowEverything() {
1993 class TimeZoneThrowEverything extends Temporal.TimeZone {
1997 getOffsetNanosecondsFor() {
1998 TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be called");
2000 getPossibleInstantsFor() {
2001 TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be called");
2004 TemporalHelpers.assertUnreachable("toString should not be called");
2008 return new TimeZoneThrowEverything();
2012 * Returns an object that will append logs of any Gets or Calls of its valueOf
2013 * or toString properties to the array calls. Both valueOf and toString will
2014 * return the actual primitiveValue. propertyName is used in the log.
2016 toPrimitiveObserver(calls, primitiveValue, propertyName) {
2019 calls.push(`get ${propertyName}.valueOf`);
2020 return function () {
2021 calls.push(`call ${propertyName}.valueOf`);
2022 return primitiveValue;
2026 calls.push(`get ${propertyName}.toString`);
2027 return function () {
2028 calls.push(`call ${propertyName}.toString`);
2029 if (primitiveValue === undefined) return undefined;
2030 return primitiveValue.toString();
2037 * An object containing further methods that return arrays of ISO strings, for
2042 * PlainMonthDay strings that are not valid.
2044 plainMonthDayStringsInvalid() {
2047 "11-18[u-ca=gregory]",
2048 "11-18[u-ca=hebrew]",
2049 "11-18[U-CA=iso8601]",
2050 "11-18[u-CA=iso8601]",
2056 * PlainMonthDay strings that are valid and that should produce October 1st.
2058 plainMonthDayStringsValid() {
2063 "1976-10-01T152330.1+00:00",
2064 "19761001T15:23:30.1+00:00",
2065 "1976-10-01T15:23:30.1+0000",
2066 "1976-10-01T152330.1+0000",
2067 "19761001T15:23:30.1+0000",
2068 "19761001T152330.1+00:00",
2069 "19761001T152330.1+0000",
2070 "+001976-10-01T152330.1+00:00",
2071 "+0019761001T15:23:30.1+00:00",
2072 "+001976-10-01T15:23:30.1+0000",
2073 "+001976-10-01T152330.1+0000",
2074 "+0019761001T15:23:30.1+0000",
2075 "+0019761001T152330.1+00:00",
2076 "+0019761001T152330.1+0000",
2077 "1976-10-01T15:23:00",
2087 * PlainTime strings that may be mistaken for PlainMonthDay or
2088 * PlainYearMonth strings, and so require a time designator.
2090 plainTimeStringsAmbiguous() {
2091 const ambiguousStrings = [
2092 "2021-12", // ambiguity between YYYY-MM and HHMM-UU
2093 "2021-12[-12:00]", // ditto, TZ does not disambiguate
2094 "1214", // ambiguity between MMDD and HHMM
2095 "0229", // ditto, including MMDD that doesn't occur every year
2096 "1130", // ditto, including DD that doesn't occur in every month
2097 "12-14", // ambiguity between MM-DD and HH-UU
2098 "12-14[-14:00]", // ditto, TZ does not disambiguate
2099 "202112", // ambiguity between YYYYMM and HHMMSS
2100 "202112[UTC]", // ditto, TZ does not disambiguate
2102 // Adding a calendar annotation to one of these strings must not cause
2103 // disambiguation in favour of time.
2104 const stringsWithCalendar = ambiguousStrings.map((s) => s + '[u-ca=iso8601]');
2105 return ambiguousStrings.concat(stringsWithCalendar);
2109 * PlainTime strings that are of similar form to PlainMonthDay and
2110 * PlainYearMonth strings, but are not ambiguous due to components that
2111 * aren't valid as months or days.
2113 plainTimeStringsUnambiguous() {
2115 "2021-13", // 13 is not a month
2117 "2021-13[-13:00]", // ditto
2118 "202113[-13:00]", // ditto
2119 "0000-00", // 0 is not a month
2121 "0000-00[UTC]", // ditto
2122 "000000[UTC]", // ditto
2123 "1314", // 13 is not a month
2125 "1232", // 32 is not a day
2126 "0230", // 30 is not a day in February
2127 "0631", // 31 is not a day in June
2128 "0000", // 0 is neither a month nor a day
2134 * PlainYearMonth-like strings that are not valid.
2136 plainYearMonthStringsInvalid() {
2139 "1976-11[u-ca=gregory]",
2140 "1976-11[u-ca=hebrew]",
2141 "1976-11[U-CA=iso8601]",
2142 "1976-11[u-CA=iso8601]",
2148 * PlainYearMonth-like strings that are valid and should produce November
2149 * 1976 in the ISO 8601 calendar.
2151 plainYearMonthStringsValid() {
2155 "1976-11-01T09:00:00+00:00",
2156 "1976-11-01T00:00:00+05:00",
2159 "1976-11-18T15:23:30.1\u221202:00",
2160 "1976-11-18T152330.1+00:00",
2161 "19761118T15:23:30.1+00:00",
2162 "1976-11-18T15:23:30.1+0000",
2163 "1976-11-18T152330.1+0000",
2164 "19761118T15:23:30.1+0000",
2165 "19761118T152330.1+00:00",
2166 "19761118T152330.1+0000",
2167 "+001976-11-18T152330.1+00:00",
2168 "+0019761118T15:23:30.1+00:00",
2169 "+001976-11-18T15:23:30.1+0000",
2170 "+001976-11-18T152330.1+0000",
2171 "+0019761118T15:23:30.1+0000",
2172 "+0019761118T152330.1+00:00",
2173 "+0019761118T152330.1+0000",
2181 * PlainYearMonth-like strings that are valid and should produce November of
2182 * the ISO year -9999.
2184 plainYearMonthStringsValidNegativeYear() {