Bug 1892041 - Part 2: Update test262. r=spidermonkey-reviewers,dminor
[gecko.git] / js / src / tests / test262 / intl402 / Temporal / TimeZone / prototype / getPlainDateTimeFor / shell.js
blob5265d86aa2ad9406fcd5b96fd84ddd54d168c040
1 // GENERATED, DO NOT EDIT
2 // file: temporalHelpers.js
3 // Copyright (C) 2021 Igalia, S.L. All rights reserved.
4 // This code is governed by the BSD license found in the LICENSE file.
5 /*---
6 description: |
7     This defines helper objects and functions for testing Temporal.
8 defines: [TemporalHelpers]
9 features: [Symbol.species, Symbol.iterator, Temporal]
10 ---*/
12 const ASCII_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/u;
14 function formatPropertyName(propertyKey, objectName = "") {
15   switch (typeof propertyKey) {
16     case "symbol":
17       if (Symbol.keyFor(propertyKey) !== undefined) {
18         return `${objectName}[Symbol.for('${Symbol.keyFor(propertyKey)}')]`;
19       } else if (propertyKey.description.startsWith('Symbol.')) {
20         return `${objectName}[${propertyKey.description}]`;
21       } else {
22         return `${objectName}[Symbol('${propertyKey.description}')]`
23       }
24     case "string":
25       if (propertyKey !== String(Number(propertyKey))) {
26         if (ASCII_IDENTIFIER.test(propertyKey)) {
27           return objectName ? `${objectName}.${propertyKey}` : propertyKey;
28         }
29         return `${objectName}['${propertyKey.replace(/'/g, "\\'")}']`
30       }
31       // fall through
32     default:
33       // integer or string integer-index
34       return `${objectName}[${propertyKey}]`;
35   }
38 const SKIP_SYMBOL = Symbol("Skip");
40 var TemporalHelpers = {
41   /*
42    * Codes and maximum lengths of months in the ISO 8601 calendar.
43    */
44   ISOMonths: [
45     { month: 1, monthCode: "M01", daysInMonth: 31 },
46     { month: 2, monthCode: "M02", daysInMonth: 29 },
47     { month: 3, monthCode: "M03", daysInMonth: 31 },
48     { month: 4, monthCode: "M04", daysInMonth: 30 },
49     { month: 5, monthCode: "M05", daysInMonth: 31 },
50     { month: 6, monthCode: "M06", daysInMonth: 30 },
51     { month: 7, monthCode: "M07", daysInMonth: 31 },
52     { month: 8, monthCode: "M08", daysInMonth: 31 },
53     { month: 9, monthCode: "M09", daysInMonth: 30 },
54     { month: 10, monthCode: "M10", daysInMonth: 31 },
55     { month: 11, monthCode: "M11", daysInMonth: 30 },
56     { month: 12, monthCode: "M12", daysInMonth: 31 }
57   ],
59   /*
60    * assertDuration(duration, years, ...,  nanoseconds[, description]):
61    *
62    * Shorthand for asserting that each field of a Temporal.Duration is equal to
63    * an expected value.
64    */
65   assertDuration(duration, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, description = "") {
66     const prefix = description ? `${description}: ` : "";
67     assert(duration instanceof Temporal.Duration, `${prefix}instanceof`);
68     assert.sameValue(duration.years, years, `${prefix}years result:`);
69     assert.sameValue(duration.months, months, `${prefix}months result:`);
70     assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`);
71     assert.sameValue(duration.days, days, `${prefix}days result:`);
72     assert.sameValue(duration.hours, hours, `${prefix}hours result:`);
73     assert.sameValue(duration.minutes, minutes, `${prefix}minutes result:`);
74     assert.sameValue(duration.seconds, seconds, `${prefix}seconds result:`);
75     assert.sameValue(duration.milliseconds, milliseconds, `${prefix}milliseconds result:`);
76     assert.sameValue(duration.microseconds, microseconds, `${prefix}microseconds result:`);
77     assert.sameValue(duration.nanoseconds, nanoseconds, `${prefix}nanoseconds result`);
78   },
80   /*
81    * assertDateDuration(duration, years, months, weeks, days, [, description]):
82    *
83    * Shorthand for asserting that each date field of a Temporal.Duration is
84    * equal to an expected value.
85    */
86   assertDateDuration(duration, years, months, weeks, days, description = "") {
87     const prefix = description ? `${description}: ` : "";
88     assert(duration instanceof Temporal.Duration, `${prefix}instanceof`);
89     assert.sameValue(duration.years, years, `${prefix}years result:`);
90     assert.sameValue(duration.months, months, `${prefix}months result:`);
91     assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`);
92     assert.sameValue(duration.days, days, `${prefix}days result:`);
93     assert.sameValue(duration.hours, 0, `${prefix}hours result should be zero:`);
94     assert.sameValue(duration.minutes, 0, `${prefix}minutes result should be zero:`);
95     assert.sameValue(duration.seconds, 0, `${prefix}seconds result should be zero:`);
96     assert.sameValue(duration.milliseconds, 0, `${prefix}milliseconds result should be zero:`);
97     assert.sameValue(duration.microseconds, 0, `${prefix}microseconds result should be zero:`);
98     assert.sameValue(duration.nanoseconds, 0, `${prefix}nanoseconds result should be zero:`);
99   },
101   /*
102    * assertDurationsEqual(actual, expected[, description]):
103    *
104    * Shorthand for asserting that each field of a Temporal.Duration is equal to
105    * the corresponding field in another Temporal.Duration.
106    */
107   assertDurationsEqual(actual, expected, description = "") {
108     const prefix = description ? `${description}: ` : "";
109     assert(expected instanceof Temporal.Duration, `${prefix}expected value should be a Temporal.Duration`);
110     TemporalHelpers.assertDuration(actual, expected.years, expected.months, expected.weeks, expected.days, expected.hours, expected.minutes, expected.seconds, expected.milliseconds, expected.microseconds, expected.nanoseconds, description);
111   },
113   /*
114    * assertInstantsEqual(actual, expected[, description]):
115    *
116    * Shorthand for asserting that two Temporal.Instants are of the correct type
117    * and equal according to their equals() methods.
118    */
119   assertInstantsEqual(actual, expected, description = "") {
120     const prefix = description ? `${description}: ` : "";
121     assert(expected instanceof Temporal.Instant, `${prefix}expected value should be a Temporal.Instant`);
122     assert(actual instanceof Temporal.Instant, `${prefix}instanceof`);
123     assert(actual.equals(expected), `${prefix}equals method`);
124   },
126   /*
127    * assertPlainDate(date, year, ..., nanosecond[, description[, era, eraYear]]):
128    *
129    * Shorthand for asserting that each field of a Temporal.PlainDate is equal to
130    * an expected value. (Except the `calendar` property, since callers may want
131    * to assert either object equality with an object they put in there, or the
132    * value of date.calendarId.)
133    */
134   assertPlainDate(date, year, month, monthCode, day, description = "", era = undefined, eraYear = undefined) {
135     const prefix = description ? `${description}: ` : "";
136     assert(date instanceof Temporal.PlainDate, `${prefix}instanceof`);
137     assert.sameValue(date.era, era, `${prefix}era result:`);
138     assert.sameValue(date.eraYear, eraYear, `${prefix}eraYear result:`);
139     assert.sameValue(date.year, year, `${prefix}year result:`);
140     assert.sameValue(date.month, month, `${prefix}month result:`);
141     assert.sameValue(date.monthCode, monthCode, `${prefix}monthCode result:`);
142     assert.sameValue(date.day, day, `${prefix}day result:`);
143   },
145   /*
146    * assertPlainDateTime(datetime, year, ..., nanosecond[, description[, era, eraYear]]):
147    *
148    * Shorthand for asserting that each field of a Temporal.PlainDateTime is
149    * equal to an expected value. (Except the `calendar` property, since callers
150    * may want to assert either object equality with an object they put in there,
151    * or the value of datetime.calendarId.)
152    */
153   assertPlainDateTime(datetime, year, month, monthCode, day, hour, minute, second, millisecond, microsecond, nanosecond, description = "", era = undefined, eraYear = undefined) {
154     const prefix = description ? `${description}: ` : "";
155     assert(datetime instanceof Temporal.PlainDateTime, `${prefix}instanceof`);
156     assert.sameValue(datetime.era, era, `${prefix}era result:`);
157     assert.sameValue(datetime.eraYear, eraYear, `${prefix}eraYear result:`);
158     assert.sameValue(datetime.year, year, `${prefix}year result:`);
159     assert.sameValue(datetime.month, month, `${prefix}month result:`);
160     assert.sameValue(datetime.monthCode, monthCode, `${prefix}monthCode result:`);
161     assert.sameValue(datetime.day, day, `${prefix}day result:`);
162     assert.sameValue(datetime.hour, hour, `${prefix}hour result:`);
163     assert.sameValue(datetime.minute, minute, `${prefix}minute result:`);
164     assert.sameValue(datetime.second, second, `${prefix}second result:`);
165     assert.sameValue(datetime.millisecond, millisecond, `${prefix}millisecond result:`);
166     assert.sameValue(datetime.microsecond, microsecond, `${prefix}microsecond result:`);
167     assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`);
168   },
170   /*
171    * assertPlainDateTimesEqual(actual, expected[, description]):
172    *
173    * Shorthand for asserting that two Temporal.PlainDateTimes are of the correct
174    * type, equal according to their equals() methods, and additionally that
175    * their calendar internal slots are the same value.
176    */
177   assertPlainDateTimesEqual(actual, expected, description = "") {
178     const prefix = description ? `${description}: ` : "";
179     assert(expected instanceof Temporal.PlainDateTime, `${prefix}expected value should be a Temporal.PlainDateTime`);
180     assert(actual instanceof Temporal.PlainDateTime, `${prefix}instanceof`);
181     assert(actual.equals(expected), `${prefix}equals method`);
182     assert.sameValue(
183       actual.getISOFields().calendar,
184       expected.getISOFields().calendar,
185       `${prefix}calendar same value:`
186     );
187   },
189   /*
190    * assertPlainMonthDay(monthDay, monthCode, day[, description [, referenceISOYear]]):
191    *
192    * Shorthand for asserting that each field of a Temporal.PlainMonthDay is
193    * equal to an expected value. (Except the `calendar` property, since callers
194    * may want to assert either object equality with an object they put in there,
195    * or the value of monthDay.calendarId().)
196    */
197   assertPlainMonthDay(monthDay, monthCode, day, description = "", referenceISOYear = 1972) {
198     const prefix = description ? `${description}: ` : "";
199     assert(monthDay instanceof Temporal.PlainMonthDay, `${prefix}instanceof`);
200     assert.sameValue(monthDay.monthCode, monthCode, `${prefix}monthCode result:`);
201     assert.sameValue(monthDay.day, day, `${prefix}day result:`);
202     assert.sameValue(monthDay.getISOFields().isoYear, referenceISOYear, `${prefix}referenceISOYear result:`);
203   },
205   /*
206    * assertPlainTime(time, hour, ..., nanosecond[, description]):
207    *
208    * Shorthand for asserting that each field of a Temporal.PlainTime is equal to
209    * an expected value.
210    */
211   assertPlainTime(time, hour, minute, second, millisecond, microsecond, nanosecond, description = "") {
212     const prefix = description ? `${description}: ` : "";
213     assert(time instanceof Temporal.PlainTime, `${prefix}instanceof`);
214     assert.sameValue(time.hour, hour, `${prefix}hour result:`);
215     assert.sameValue(time.minute, minute, `${prefix}minute result:`);
216     assert.sameValue(time.second, second, `${prefix}second result:`);
217     assert.sameValue(time.millisecond, millisecond, `${prefix}millisecond result:`);
218     assert.sameValue(time.microsecond, microsecond, `${prefix}microsecond result:`);
219     assert.sameValue(time.nanosecond, nanosecond, `${prefix}nanosecond result:`);
220   },
222   /*
223    * assertPlainTimesEqual(actual, expected[, description]):
224    *
225    * Shorthand for asserting that two Temporal.PlainTimes are of the correct
226    * type and equal according to their equals() methods.
227    */
228   assertPlainTimesEqual(actual, expected, description = "") {
229     const prefix = description ? `${description}: ` : "";
230     assert(expected instanceof Temporal.PlainTime, `${prefix}expected value should be a Temporal.PlainTime`);
231     assert(actual instanceof Temporal.PlainTime, `${prefix}instanceof`);
232     assert(actual.equals(expected), `${prefix}equals method`);
233   },
235   /*
236    * assertPlainYearMonth(yearMonth, year, month, monthCode[, description[, era, eraYear, referenceISODay]]):
237    *
238    * Shorthand for asserting that each field of a Temporal.PlainYearMonth is
239    * equal to an expected value. (Except the `calendar` property, since callers
240    * may want to assert either object equality with an object they put in there,
241    * or the value of yearMonth.calendarId.)
242    */
243   assertPlainYearMonth(yearMonth, year, month, monthCode, description = "", era = undefined, eraYear = undefined, referenceISODay = 1) {
244     const prefix = description ? `${description}: ` : "";
245     assert(yearMonth instanceof Temporal.PlainYearMonth, `${prefix}instanceof`);
246     assert.sameValue(yearMonth.era, era, `${prefix}era result:`);
247     assert.sameValue(yearMonth.eraYear, eraYear, `${prefix}eraYear result:`);
248     assert.sameValue(yearMonth.year, year, `${prefix}year result:`);
249     assert.sameValue(yearMonth.month, month, `${prefix}month result:`);
250     assert.sameValue(yearMonth.monthCode, monthCode, `${prefix}monthCode result:`);
251     assert.sameValue(yearMonth.getISOFields().isoDay, referenceISODay, `${prefix}referenceISODay result:`);
252   },
254   /*
255    * assertZonedDateTimesEqual(actual, expected[, description]):
256    *
257    * Shorthand for asserting that two Temporal.ZonedDateTimes are of the correct
258    * type, equal according to their equals() methods, and additionally that
259    * their time zones and calendar internal slots are the same value.
260    */
261   assertZonedDateTimesEqual(actual, expected, description = "") {
262     const prefix = description ? `${description}: ` : "";
263     assert(expected instanceof Temporal.ZonedDateTime, `${prefix}expected value should be a Temporal.ZonedDateTime`);
264     assert(actual instanceof Temporal.ZonedDateTime, `${prefix}instanceof`);
265     assert(actual.equals(expected), `${prefix}equals method`);
266     assert.sameValue(actual.timeZone, expected.timeZone, `${prefix}time zone same value:`);
267     assert.sameValue(
268       actual.getISOFields().calendar,
269       expected.getISOFields().calendar,
270       `${prefix}calendar same value:`
271     );
272   },
274   /*
275    * assertUnreachable(description):
276    *
277    * Helper for asserting that code is not executed. This is useful for
278    * assertions that methods of user calendars and time zones are not called.
279    */
280   assertUnreachable(description) {
281     let message = "This code should not be executed";
282     if (description) {
283       message = `${message}: ${description}`;
284     }
285     throw new Test262Error(message);
286   },
288   /*
289    * checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls):
290    *
291    * When an options object with a largestUnit property is synthesized inside
292    * Temporal and passed to user code such as calendar.dateUntil(), the value of
293    * the largestUnit property should be in the singular form, even if the input
294    * was given in the plural form.
295    * (This doesn't apply when the options object is passed through verbatim.)
296    *
297    * func(calendar, largestUnit, index) is the operation under test. It's called
298    * with an instance of a calendar that keeps track of which largestUnit is
299    * passed to dateUntil(), each key of expectedLargestUnitCalls in turn, and
300    * the key's numerical index in case the function needs to generate test data
301    * based on the index. At the end, the actual values passed to dateUntil() are
302    * compared with the array values of expectedLargestUnitCalls.
303    */
304   checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls) {
305     const actual = [];
307     class DateUntilOptionsCalendar extends Temporal.Calendar {
308       constructor() {
309         super("iso8601");
310       }
312       dateUntil(earlier, later, options) {
313         actual.push(options.largestUnit);
314         return super.dateUntil(earlier, later, options);
315       }
317       toString() {
318         return "date-until-options";
319       }
320     }
322     const calendar = new DateUntilOptionsCalendar();
323     Object.entries(expectedLargestUnitCalls).forEach(([largestUnit, expected], index) => {
324       func(calendar, largestUnit, index);
325       assert.compareArray(actual, expected, `largestUnit passed to calendar.dateUntil() for largestUnit ${largestUnit}`);
326       actual.splice(0); // empty it for the next check
327     });
328   },
330   /*
331    * checkPlainDateTimeConversionFastPath(func):
332    *
333    * ToTemporalDate and ToTemporalTime should both, if given a
334    * Temporal.PlainDateTime instance, convert to the desired type by reading the
335    * PlainDateTime's internal slots, rather than calling any getters.
336    *
337    * func(datetime, calendar) is the actual operation to test, that must
338    * internally call the abstract operation ToTemporalDate or ToTemporalTime.
339    * It is passed a Temporal.PlainDateTime instance, as well as the instance's
340    * calendar object (so that it doesn't have to call the calendar getter itself
341    * if it wants to make any assertions about the calendar.)
342    */
343   checkPlainDateTimeConversionFastPath(func, message = "checkPlainDateTimeConversionFastPath") {
344     const actual = [];
345     const expected = [];
347     const calendar = new Temporal.Calendar("iso8601");
348     const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
349     const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDateTime.prototype);
350     ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => {
351       Object.defineProperty(datetime, property, {
352         get() {
353           actual.push(`get ${formatPropertyName(property)}`);
354           const value = prototypeDescrs[property].get.call(this);
355           return {
356             toString() {
357               actual.push(`toString ${formatPropertyName(property)}`);
358               return value.toString();
359             },
360             valueOf() {
361               actual.push(`valueOf ${formatPropertyName(property)}`);
362               return value;
363             },
364           };
365         },
366       });
367     });
368     Object.defineProperty(datetime, "calendar", {
369       get() {
370         actual.push("get calendar");
371         return calendar;
372       },
373     });
375     func(datetime, calendar);
376     assert.compareArray(actual, expected, `${message}: property getters not called`);
377   },
379   /*
380    * Check that an options bag that accepts units written in the singular form,
381    * also accepts the same units written in the plural form.
382    * func(unit) should call the method with the appropriate options bag
383    * containing unit as a value. This will be called twice for each element of
384    * validSingularUnits, once with singular and once with plural, and the
385    * results of each pair should be the same (whether a Temporal object or a
386    * primitive value.)
387    */
388   checkPluralUnitsAccepted(func, validSingularUnits) {
389     const plurals = {
390       year: 'years',
391       month: 'months',
392       week: 'weeks',
393       day: 'days',
394       hour: 'hours',
395       minute: 'minutes',
396       second: 'seconds',
397       millisecond: 'milliseconds',
398       microsecond: 'microseconds',
399       nanosecond: 'nanoseconds',
400     };
402     validSingularUnits.forEach((unit) => {
403       const singularValue = func(unit);
404       const pluralValue = func(plurals[unit]);
405       const desc = `Plural ${plurals[unit]} produces the same result as singular ${unit}`;
406       if (singularValue instanceof Temporal.Duration) {
407         TemporalHelpers.assertDurationsEqual(pluralValue, singularValue, desc);
408       } else if (singularValue instanceof Temporal.Instant) {
409         TemporalHelpers.assertInstantsEqual(pluralValue, singularValue, desc);
410       } else if (singularValue instanceof Temporal.PlainDateTime) {
411         TemporalHelpers.assertPlainDateTimesEqual(pluralValue, singularValue, desc);
412       } else if (singularValue instanceof Temporal.PlainTime) {
413         TemporalHelpers.assertPlainTimesEqual(pluralValue, singularValue, desc);
414       } else if (singularValue instanceof Temporal.ZonedDateTime) {
415         TemporalHelpers.assertZonedDateTimesEqual(pluralValue, singularValue, desc);
416       } else {
417         assert.sameValue(pluralValue, singularValue);
418       }
419     });
420   },
422   /*
423    * checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc):
424    *
425    * Checks the type handling of the roundingIncrement option.
426    * checkFunc(roundingIncrement) is a function which takes the value of
427    * roundingIncrement to test, and calls the method under test with it,
428    * returning the result. assertTrueResultFunc(result, description) should
429    * assert that result is the expected result with roundingIncrement: true, and
430    * assertObjectResultFunc(result, description) should assert that result is
431    * the expected result with roundingIncrement being an object with a valueOf()
432    * method.
433    */
434   checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc) {
435     // null converts to 0, which is out of range
436     assert.throws(RangeError, () => checkFunc(null), "null");
437     // Booleans convert to either 0 or 1, and 1 is allowed
438     const trueResult = checkFunc(true);
439     assertTrueResultFunc(trueResult, "true");
440     assert.throws(RangeError, () => checkFunc(false), "false");
441     // Symbols and BigInts cannot convert to numbers
442     assert.throws(TypeError, () => checkFunc(Symbol()), "symbol");
443     assert.throws(TypeError, () => checkFunc(2n), "bigint");
445     // Objects prefer their valueOf() methods when converting to a number
446     assert.throws(RangeError, () => checkFunc({}), "plain object");
448     const expected = [
449       "get roundingIncrement.valueOf",
450       "call roundingIncrement.valueOf",
451     ];
452     const actual = [];
453     const observer = TemporalHelpers.toPrimitiveObserver(actual, 2, "roundingIncrement");
454     const objectResult = checkFunc(observer);
455     assertObjectResultFunc(objectResult, "object with valueOf");
456     assert.compareArray(actual, expected, "order of operations");
457   },
459   /*
460    * checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc):
461    *
462    * Checks the type handling of a string option, of which there are several in
463    * Temporal.
464    * propertyName is the name of the option, and value is the value that
465    * assertFunc should expect it to have.
466    * checkFunc(value) is a function which takes the value of the option to test,
467    * and calls the method under test with it, returning the result.
468    * assertFunc(result, description) should assert that result is the expected
469    * result with the option value being an object with a toString() method
470    * which returns the given value.
471    */
472   checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc) {
473     // null converts to the string "null", which is an invalid string value
474     assert.throws(RangeError, () => checkFunc(null), "null");
475     // Booleans convert to the strings "true" or "false", which are invalid
476     assert.throws(RangeError, () => checkFunc(true), "true");
477     assert.throws(RangeError, () => checkFunc(false), "false");
478     // Symbols cannot convert to strings
479     assert.throws(TypeError, () => checkFunc(Symbol()), "symbol");
480     // Numbers convert to strings which are invalid
481     assert.throws(RangeError, () => checkFunc(2), "number");
482     // BigInts convert to strings which are invalid
483     assert.throws(RangeError, () => checkFunc(2n), "bigint");
485     // Objects prefer their toString() methods when converting to a string
486     assert.throws(RangeError, () => checkFunc({}), "plain object");
488     const expected = [
489       `get ${propertyName}.toString`,
490       `call ${propertyName}.toString`,
491     ];
492     const actual = [];
493     const observer = TemporalHelpers.toPrimitiveObserver(actual, value, propertyName);
494     const result = checkFunc(observer);
495     assertFunc(result, "object with toString");
496     assert.compareArray(actual, expected, "order of operations");
497   },
499   /*
500    * checkSubclassingIgnored(construct, constructArgs, method, methodArgs,
501    *   resultAssertions):
502    *
503    * Methods of Temporal classes that return a new instance of the same class,
504    * must not take the constructor of a subclass into account, nor the @@species
505    * property. This helper runs tests to ensure this.
506    *
507    * construct(...constructArgs) must yield a valid instance of the Temporal
508    * class. instance[method](...methodArgs) is the method call under test, which
509    * must also yield a valid instance of the same Temporal class, not a
510    * subclass. See below for the individual tests that this runs.
511    * resultAssertions() is a function that performs additional assertions on the
512    * instance returned by the method under test.
513    */
514   checkSubclassingIgnored(...args) {
515     this.checkSubclassConstructorNotObject(...args);
516     this.checkSubclassConstructorUndefined(...args);
517     this.checkSubclassConstructorThrows(...args);
518     this.checkSubclassConstructorNotCalled(...args);
519     this.checkSubclassSpeciesInvalidResult(...args);
520     this.checkSubclassSpeciesNotAConstructor(...args);
521     this.checkSubclassSpeciesNull(...args);
522     this.checkSubclassSpeciesUndefined(...args);
523     this.checkSubclassSpeciesThrows(...args);
524   },
526   /*
527    * Checks that replacing the 'constructor' property of the instance with
528    * various primitive values does not affect the returned new instance.
529    */
530   checkSubclassConstructorNotObject(construct, constructArgs, method, methodArgs, resultAssertions) {
531     function check(value, description) {
532       const instance = new construct(...constructArgs);
533       instance.constructor = value;
534       const result = instance[method](...methodArgs);
535       assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
536       resultAssertions(result);
537     }
539     check(null, "null");
540     check(true, "true");
541     check("test", "string");
542     check(Symbol(), "Symbol");
543     check(7, "number");
544     check(7n, "bigint");
545   },
547   /*
548    * Checks that replacing the 'constructor' property of the subclass with
549    * undefined does not affect the returned new instance.
550    */
551   checkSubclassConstructorUndefined(construct, constructArgs, method, methodArgs, resultAssertions) {
552     let called = 0;
554     class MySubclass extends construct {
555       constructor() {
556         ++called;
557         super(...constructArgs);
558       }
559     }
561     const instance = new MySubclass();
562     assert.sameValue(called, 1);
564     MySubclass.prototype.constructor = undefined;
566     const result = instance[method](...methodArgs);
567     assert.sameValue(called, 1);
568     assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
569     resultAssertions(result);
570   },
572   /*
573    * Checks that making the 'constructor' property of the instance throw when
574    * called does not affect the returned new instance.
575    */
576   checkSubclassConstructorThrows(construct, constructArgs, method, methodArgs, resultAssertions) {
577     function CustomError() {}
578     const instance = new construct(...constructArgs);
579     Object.defineProperty(instance, "constructor", {
580       get() {
581         throw new CustomError();
582       }
583     });
584     const result = instance[method](...methodArgs);
585     assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
586     resultAssertions(result);
587   },
589   /*
590    * Checks that when subclassing, the subclass constructor is not called by
591    * the method under test.
592    */
593   checkSubclassConstructorNotCalled(construct, constructArgs, method, methodArgs, resultAssertions) {
594     let called = 0;
596     class MySubclass extends construct {
597       constructor() {
598         ++called;
599         super(...constructArgs);
600       }
601     }
603     const instance = new MySubclass();
604     assert.sameValue(called, 1);
606     const result = instance[method](...methodArgs);
607     assert.sameValue(called, 1);
608     assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
609     resultAssertions(result);
610   },
612   /*
613    * Check that the constructor's @@species property is ignored when it's a
614    * constructor that returns a non-object value.
615    */
616   checkSubclassSpeciesInvalidResult(construct, constructArgs, method, methodArgs, resultAssertions) {
617     function check(value, description) {
618       const instance = new construct(...constructArgs);
619       instance.constructor = {
620         [Symbol.species]: function() {
621           return value;
622         },
623       };
624       const result = instance[method](...methodArgs);
625       assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
626       resultAssertions(result);
627     }
629     check(undefined, "undefined");
630     check(null, "null");
631     check(true, "true");
632     check("test", "string");
633     check(Symbol(), "Symbol");
634     check(7, "number");
635     check(7n, "bigint");
636     check({}, "plain object");
637   },
639   /*
640    * Check that the constructor's @@species property is ignored when it's not a
641    * constructor.
642    */
643   checkSubclassSpeciesNotAConstructor(construct, constructArgs, method, methodArgs, resultAssertions) {
644     function check(value, description) {
645       const instance = new construct(...constructArgs);
646       instance.constructor = {
647         [Symbol.species]: value,
648       };
649       const result = instance[method](...methodArgs);
650       assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
651       resultAssertions(result);
652     }
654     check(true, "true");
655     check("test", "string");
656     check(Symbol(), "Symbol");
657     check(7, "number");
658     check(7n, "bigint");
659     check({}, "plain object");
660   },
662   /*
663    * Check that the constructor's @@species property is ignored when it's null.
664    */
665   checkSubclassSpeciesNull(construct, constructArgs, method, methodArgs, resultAssertions) {
666     let called = 0;
668     class MySubclass extends construct {
669       constructor() {
670         ++called;
671         super(...constructArgs);
672       }
673     }
675     const instance = new MySubclass();
676     assert.sameValue(called, 1);
678     MySubclass.prototype.constructor = {
679       [Symbol.species]: null,
680     };
682     const result = instance[method](...methodArgs);
683     assert.sameValue(called, 1);
684     assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
685     resultAssertions(result);
686   },
688   /*
689    * Check that the constructor's @@species property is ignored when it's
690    * undefined.
691    */
692   checkSubclassSpeciesUndefined(construct, constructArgs, method, methodArgs, resultAssertions) {
693     let called = 0;
695     class MySubclass extends construct {
696       constructor() {
697         ++called;
698         super(...constructArgs);
699       }
700     }
702     const instance = new MySubclass();
703     assert.sameValue(called, 1);
705     MySubclass.prototype.constructor = {
706       [Symbol.species]: undefined,
707     };
709     const result = instance[method](...methodArgs);
710     assert.sameValue(called, 1);
711     assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
712     resultAssertions(result);
713   },
715   /*
716    * Check that the constructor's @@species property is ignored when it throws,
717    * i.e. it is not called at all.
718    */
719   checkSubclassSpeciesThrows(construct, constructArgs, method, methodArgs, resultAssertions) {
720     function CustomError() {}
722     const instance = new construct(...constructArgs);
723     instance.constructor = {
724       get [Symbol.species]() {
725         throw new CustomError();
726       },
727     };
729     const result = instance[method](...methodArgs);
730     assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
731   },
733   /*
734    * checkSubclassingIgnoredStatic(construct, method, methodArgs, resultAssertions):
735    *
736    * Static methods of Temporal classes that return a new instance of the class,
737    * must not use the this-value as a constructor. This helper runs tests to
738    * ensure this.
739    *
740    * construct[method](...methodArgs) is the static method call under test, and
741    * must yield a valid instance of the Temporal class, not a subclass. See
742    * below for the individual tests that this runs.
743    * resultAssertions() is a function that performs additional assertions on the
744    * instance returned by the method under test.
745    */
746   checkSubclassingIgnoredStatic(...args) {
747     this.checkStaticInvalidReceiver(...args);
748     this.checkStaticReceiverNotCalled(...args);
749     this.checkThisValueNotCalled(...args);
750   },
752   /*
753    * Check that calling the static method with a receiver that's not callable,
754    * still calls the intrinsic constructor.
755    */
756   checkStaticInvalidReceiver(construct, method, methodArgs, resultAssertions) {
757     function check(value, description) {
758       const result = construct[method].apply(value, methodArgs);
759       assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
760       resultAssertions(result);
761     }
763     check(undefined, "undefined");
764     check(null, "null");
765     check(true, "true");
766     check("test", "string");
767     check(Symbol(), "symbol");
768     check(7, "number");
769     check(7n, "bigint");
770     check({}, "Non-callable object");
771   },
773   /*
774    * Check that calling the static method with a receiver that returns a value
775    * that's not callable, still calls the intrinsic constructor.
776    */
777   checkStaticReceiverNotCalled(construct, method, methodArgs, resultAssertions) {
778     function check(value, description) {
779       const receiver = function () {
780         return value;
781       };
782       const result = construct[method].apply(receiver, methodArgs);
783       assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
784       resultAssertions(result);
785     }
787     check(undefined, "undefined");
788     check(null, "null");
789     check(true, "true");
790     check("test", "string");
791     check(Symbol(), "symbol");
792     check(7, "number");
793     check(7n, "bigint");
794     check({}, "Non-callable object");
795   },
797   /*
798    * Check that the receiver isn't called.
799    */
800   checkThisValueNotCalled(construct, method, methodArgs, resultAssertions) {
801     let called = false;
803     class MySubclass extends construct {
804       constructor(...args) {
805         called = true;
806         super(...args);
807       }
808     }
810     const result = MySubclass[method](...methodArgs);
811     assert.sameValue(called, false);
812     assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
813     resultAssertions(result);
814   },
816   /*
817    * Check that any iterable returned from a custom time zone's
818    * getPossibleInstantsFor() method is exhausted.
819    * The custom time zone object is passed in to func().
820    * expected is an array of strings representing the expected calls to the
821    * getPossibleInstantsFor() method. The PlainDateTimes that it is called with,
822    * are compared (using their toString() results) with the array.
823    */
824   checkTimeZonePossibleInstantsIterable(func, expected) {
825     // A custom time zone that returns an iterable instead of an array from its
826     // getPossibleInstantsFor() method, and for testing purposes skips
827     // 00:00-01:00 UTC on January 1, 2030, and repeats 00:00-01:00 UTC+1 on
828     // January 3, 2030. Otherwise identical to the UTC time zone.
829     class TimeZonePossibleInstantsIterable extends Temporal.TimeZone {
830       constructor() {
831         super("UTC");
832         this.getPossibleInstantsForCallCount = 0;
833         this.getPossibleInstantsForCalledWith = [];
834         this.getPossibleInstantsForReturns = [];
835         this.iteratorExhausted = [];
836       }
838       toString() {
839         return "Custom/Iterable";
840       }
842       getOffsetNanosecondsFor(instant) {
843         if (Temporal.Instant.compare(instant, "2030-01-01T00:00Z") >= 0 &&
844           Temporal.Instant.compare(instant, "2030-01-03T01:00Z") < 0) {
845           return 3600_000_000_000;
846         } else {
847           return 0;
848         }
849       }
851       getPossibleInstantsFor(dateTime) {
852         this.getPossibleInstantsForCallCount++;
853         this.getPossibleInstantsForCalledWith.push(dateTime);
855         // Fake DST transition
856         let retval = super.getPossibleInstantsFor(dateTime);
857         if (dateTime.toPlainDate().equals("2030-01-01") && dateTime.hour === 0) {
858           retval = [];
859         } else if (dateTime.toPlainDate().equals("2030-01-03") && dateTime.hour === 0) {
860           retval.push(retval[0].subtract({ hours: 1 }));
861         } else if (dateTime.year === 2030 && dateTime.month === 1 && dateTime.day >= 1 && dateTime.day <= 2) {
862           retval[0] = retval[0].subtract({ hours: 1 });
863         }
865         this.getPossibleInstantsForReturns.push(retval);
866         this.iteratorExhausted.push(false);
867         return {
868           callIndex: this.getPossibleInstantsForCallCount - 1,
869           timeZone: this,
870           *[Symbol.iterator]() {
871             yield* this.timeZone.getPossibleInstantsForReturns[this.callIndex];
872             this.timeZone.iteratorExhausted[this.callIndex] = true;
873           },
874         };
875       }
876     }
878     const timeZone = new TimeZonePossibleInstantsIterable();
879     func(timeZone);
881     assert.sameValue(timeZone.getPossibleInstantsForCallCount, expected.length, "getPossibleInstantsFor() method called correct number of times");
883     for (let index = 0; index < expected.length; index++) {
884       assert.sameValue(timeZone.getPossibleInstantsForCalledWith[index].toString(), expected[index], "getPossibleInstantsFor() called with expected PlainDateTime");
885       assert(timeZone.iteratorExhausted[index], "iterated through the whole iterable");
886     }
887   },
889   /*
890    * Check that any calendar-carrying Temporal object has its [[Calendar]]
891    * internal slot read by ToTemporalCalendar, and does not fetch the calendar
892    * by calling getters.
893    * The custom calendar object is passed in to func() so that it can do its
894    * own additional assertions involving the calendar if necessary. (Sometimes
895    * there is nothing to assert as the calendar isn't stored anywhere that can
896    * be asserted about.)
897    */
898   checkToTemporalCalendarFastPath(func) {
899     class CalendarFastPathCheck extends Temporal.Calendar {
900       constructor() {
901         super("iso8601");
902       }
904       dateFromFields(...args) {
905         return super.dateFromFields(...args).withCalendar(this);
906       }
908       monthDayFromFields(...args) {
909         const { isoYear, isoMonth, isoDay } = super.monthDayFromFields(...args).getISOFields();
910         return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
911       }
913       yearMonthFromFields(...args) {
914         const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields(...args).getISOFields();
915         return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
916       }
918       toString() {
919         return "fast-path-check";
920       }
921     }
922     const calendar = new CalendarFastPathCheck();
924     const plainDate = new Temporal.PlainDate(2000, 5, 2, calendar);
925     const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
926     const plainMonthDay = new Temporal.PlainMonthDay(5, 2, calendar);
927     const plainYearMonth = new Temporal.PlainYearMonth(2000, 5, calendar);
928     const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
930     [plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((temporalObject) => {
931       const actual = [];
932       const expected = [];
934       Object.defineProperty(temporalObject, "calendar", {
935         get() {
936           actual.push("get calendar");
937           return calendar;
938         },
939       });
941       func(temporalObject, calendar);
942       assert.compareArray(actual, expected, "calendar getter not called");
943     });
944   },
946   checkToTemporalInstantFastPath(func) {
947     const actual = [];
948     const expected = [];
950     const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
951     Object.defineProperty(datetime, 'toString', {
952       get() {
953         actual.push("get toString");
954         return function (options) {
955           actual.push("call toString");
956           return Temporal.ZonedDateTime.prototype.toString.call(this, options);
957         };
958       },
959     });
961     func(datetime);
962     assert.compareArray(actual, expected, "toString not called");
963   },
965   checkToTemporalPlainDateTimeFastPath(func) {
966     const actual = [];
967     const expected = [];
969     const calendar = new Temporal.Calendar("iso8601");
970     const date = new Temporal.PlainDate(2000, 5, 2, calendar);
971     const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDate.prototype);
972     ["year", "month", "monthCode", "day"].forEach((property) => {
973       Object.defineProperty(date, property, {
974         get() {
975           actual.push(`get ${formatPropertyName(property)}`);
976           const value = prototypeDescrs[property].get.call(this);
977           return TemporalHelpers.toPrimitiveObserver(actual, value, property);
978         },
979       });
980     });
981     ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => {
982       Object.defineProperty(date, property, {
983         get() {
984           actual.push(`get ${formatPropertyName(property)}`);
985           return undefined;
986         },
987       });
988     });
989     Object.defineProperty(date, "calendar", {
990       get() {
991         actual.push("get calendar");
992         return calendar;
993       },
994     });
996     func(date, calendar);
997     assert.compareArray(actual, expected, "property getters not called");
998   },
1000   /*
1001    * A custom calendar used in prototype pollution checks. Verifies that the
1002    * fromFields methods are always called with a null-prototype fields object.
1003    */
1004   calendarCheckFieldsPrototypePollution() {
1005     class CalendarCheckFieldsPrototypePollution extends Temporal.Calendar {
1006       constructor() {
1007         super("iso8601");
1008         this.dateFromFieldsCallCount = 0;
1009         this.yearMonthFromFieldsCallCount = 0;
1010         this.monthDayFromFieldsCallCount = 0;
1011       }
1013       // toString must remain "iso8601", so that some methods don't throw due to
1014       // incompatible calendars
1016       dateFromFields(fields, options = {}) {
1017         this.dateFromFieldsCallCount++;
1018         assert.sameValue(Object.getPrototypeOf(fields), null, "dateFromFields should be called with null-prototype fields object");
1019         return super.dateFromFields(fields, options);
1020       }
1022       yearMonthFromFields(fields, options = {}) {
1023         this.yearMonthFromFieldsCallCount++;
1024         assert.sameValue(Object.getPrototypeOf(fields), null, "yearMonthFromFields should be called with null-prototype fields object");
1025         return super.yearMonthFromFields(fields, options);
1026       }
1028       monthDayFromFields(fields, options = {}) {
1029         this.monthDayFromFieldsCallCount++;
1030         assert.sameValue(Object.getPrototypeOf(fields), null, "monthDayFromFields should be called with null-prototype fields object");
1031         return super.monthDayFromFields(fields, options);
1032       }
1033     }
1035     return new CalendarCheckFieldsPrototypePollution();
1036   },
1038   /*
1039    * A custom calendar used in prototype pollution checks. Verifies that the
1040    * mergeFields() method is always called with null-prototype fields objects.
1041    */
1042   calendarCheckMergeFieldsPrototypePollution() {
1043     class CalendarCheckMergeFieldsPrototypePollution extends Temporal.Calendar {
1044       constructor() {
1045         super("iso8601");
1046         this.mergeFieldsCallCount = 0;
1047       }
1049       toString() {
1050         return "merge-fields-null-proto";
1051       }
1053       mergeFields(fields, additionalFields) {
1054         this.mergeFieldsCallCount++;
1055         assert.sameValue(Object.getPrototypeOf(fields), null, "mergeFields should be called with null-prototype fields object (first argument)");
1056         assert.sameValue(Object.getPrototypeOf(additionalFields), null, "mergeFields should be called with null-prototype fields object (second argument)");
1057         return super.mergeFields(fields, additionalFields);
1058       }
1059     }
1061     return new CalendarCheckMergeFieldsPrototypePollution();
1062   },
1064   /*
1065    * A custom calendar used in prototype pollution checks. Verifies that methods
1066    * are always called with a null-prototype options object.
1067    */
1068   calendarCheckOptionsPrototypePollution() {
1069     class CalendarCheckOptionsPrototypePollution extends Temporal.Calendar {
1070       constructor() {
1071         super("iso8601");
1072         this.yearMonthFromFieldsCallCount = 0;
1073         this.dateUntilCallCount = 0;
1074       }
1076       toString() {
1077         return "options-null-proto";
1078       }
1080       yearMonthFromFields(fields, options) {
1081         this.yearMonthFromFieldsCallCount++;
1082         assert.sameValue(Object.getPrototypeOf(options), null, "yearMonthFromFields should be called with null-prototype options");
1083         return super.yearMonthFromFields(fields, options);
1084       }
1086       dateUntil(one, two, options) {
1087         this.dateUntilCallCount++;
1088         assert.sameValue(Object.getPrototypeOf(options), null, "dateUntil should be called with null-prototype options");
1089         return super.dateUntil(one, two, options);
1090       }
1091     }
1093     return new CalendarCheckOptionsPrototypePollution();
1094   },
1096   /*
1097    * A custom calendar that asserts its dateAdd() method is called with the
1098    * options parameter having the value undefined.
1099    */
1100   calendarDateAddUndefinedOptions() {
1101     class CalendarDateAddUndefinedOptions extends Temporal.Calendar {
1102       constructor() {
1103         super("iso8601");
1104         this.dateAddCallCount = 0;
1105       }
1107       toString() {
1108         return "dateadd-undef-options";
1109       }
1111       dateAdd(date, duration, options) {
1112         this.dateAddCallCount++;
1113         assert.sameValue(options, undefined, "dateAdd shouldn't be called with options");
1114         return super.dateAdd(date, duration, options);
1115       }
1116     }
1117     return new CalendarDateAddUndefinedOptions();
1118   },
1120   /*
1121    * A custom calendar that asserts its dateAdd() method is called with a
1122    * PlainDate instance. Optionally, it also asserts that the PlainDate instance
1123    * is the specific object `this.specificPlainDate`, if it is set by the
1124    * calling code.
1125    */
1126   calendarDateAddPlainDateInstance() {
1127     class CalendarDateAddPlainDateInstance extends Temporal.Calendar {
1128       constructor() {
1129         super("iso8601");
1130         this.dateAddCallCount = 0;
1131         this.specificPlainDate = undefined;
1132       }
1134       toString() {
1135         return "dateadd-plain-date-instance";
1136       }
1138       dateFromFields(...args) {
1139         return super.dateFromFields(...args).withCalendar(this);
1140       }
1142       dateAdd(date, duration, options) {
1143         this.dateAddCallCount++;
1144         assert(date instanceof Temporal.PlainDate, "dateAdd() should be called with a PlainDate instance");
1145         if (this.dateAddCallCount === 1 && this.specificPlainDate) {
1146           assert.sameValue(date, this.specificPlainDate, `dateAdd() should be called first with the specific PlainDate instance ${this.specificPlainDate}`);
1147         }
1148         return super.dateAdd(date, duration, options).withCalendar(this);
1149       }
1150     }
1151     return new CalendarDateAddPlainDateInstance();
1152   },
1154   /*
1155    * A custom calendar that returns an iterable instead of an array from its
1156    * fields() method, otherwise identical to the ISO calendar.
1157    */
1158   calendarFieldsIterable() {
1159     class CalendarFieldsIterable extends Temporal.Calendar {
1160       constructor() {
1161         super("iso8601");
1162         this.fieldsCallCount = 0;
1163         this.fieldsCalledWith = [];
1164         this.iteratorExhausted = [];
1165       }
1167       toString() {
1168         return "fields-iterable";
1169       }
1171       fields(fieldNames) {
1172         this.fieldsCallCount++;
1173         this.fieldsCalledWith.push(fieldNames.slice());
1174         this.iteratorExhausted.push(false);
1175         return {
1176           callIndex: this.fieldsCallCount - 1,
1177           calendar: this,
1178           *[Symbol.iterator]() {
1179             yield* this.calendar.fieldsCalledWith[this.callIndex];
1180             this.calendar.iteratorExhausted[this.callIndex] = true;
1181           },
1182         };
1183       }
1184     }
1185     return new CalendarFieldsIterable();
1186   },
1188   /*
1189    * A custom calendar that asserts its ...FromFields() methods are called with
1190    * the options parameter having the value undefined.
1191    */
1192   calendarFromFieldsUndefinedOptions() {
1193     class CalendarFromFieldsUndefinedOptions extends Temporal.Calendar {
1194       constructor() {
1195         super("iso8601");
1196         this.dateFromFieldsCallCount = 0;
1197         this.monthDayFromFieldsCallCount = 0;
1198         this.yearMonthFromFieldsCallCount = 0;
1199       }
1201       toString() {
1202         return "from-fields-undef-options";
1203       }
1205       dateFromFields(fields, options) {
1206         this.dateFromFieldsCallCount++;
1207         assert.sameValue(options, undefined, "dateFromFields shouldn't be called with options");
1208         return super.dateFromFields(fields, options);
1209       }
1211       yearMonthFromFields(fields, options) {
1212         this.yearMonthFromFieldsCallCount++;
1213         assert.sameValue(options, undefined, "yearMonthFromFields shouldn't be called with options");
1214         return super.yearMonthFromFields(fields, options);
1215       }
1217       monthDayFromFields(fields, options) {
1218         this.monthDayFromFieldsCallCount++;
1219         assert.sameValue(options, undefined, "monthDayFromFields shouldn't be called with options");
1220         return super.monthDayFromFields(fields, options);
1221       }
1222     }
1223     return new CalendarFromFieldsUndefinedOptions();
1224   },
1226   /*
1227    * A custom calendar that modifies the fields object passed in to
1228    * dateFromFields, sabotaging its time properties.
1229    */
1230   calendarMakeInfinityTime() {
1231     class CalendarMakeInfinityTime extends Temporal.Calendar {
1232       constructor() {
1233         super("iso8601");
1234       }
1236       dateFromFields(fields, options) {
1237         const retval = super.dateFromFields(fields, options);
1238         fields.hour = Infinity;
1239         fields.minute = Infinity;
1240         fields.second = Infinity;
1241         fields.millisecond = Infinity;
1242         fields.microsecond = Infinity;
1243         fields.nanosecond = Infinity;
1244         return retval;
1245       }
1246     }
1247     return new CalendarMakeInfinityTime();
1248   },
1250   /*
1251    * A custom calendar that defines getters on the fields object passed into
1252    * dateFromFields that throw, sabotaging its time properties.
1253    */
1254   calendarMakeInvalidGettersTime() {
1255     class CalendarMakeInvalidGettersTime extends Temporal.Calendar {
1256       constructor() {
1257         super("iso8601");
1258       }
1260       dateFromFields(fields, options) {
1261         const retval = super.dateFromFields(fields, options);
1262         const throwingDescriptor = {
1263           get() {
1264             throw new Test262Error("reading a sabotaged time field");
1265           },
1266         };
1267         Object.defineProperties(fields, {
1268           hour: throwingDescriptor,
1269           minute: throwingDescriptor,
1270           second: throwingDescriptor,
1271           millisecond: throwingDescriptor,
1272           microsecond: throwingDescriptor,
1273           nanosecond: throwingDescriptor,
1274         });
1275         return retval;
1276       }
1277     }
1278     return new CalendarMakeInvalidGettersTime();
1279   },
1281   /*
1282    * A custom calendar whose mergeFields() method returns a proxy object with
1283    * all of its Get and HasProperty operations observable, as well as adding a
1284    * "shouldNotBeCopied": true property.
1285    */
1286   calendarMergeFieldsGetters() {
1287     class CalendarMergeFieldsGetters extends Temporal.Calendar {
1288       constructor() {
1289         super("iso8601");
1290         this.mergeFieldsReturnOperations = [];
1291       }
1293       toString() {
1294         return "merge-fields-getters";
1295       }
1297       dateFromFields(fields, options) {
1298         assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
1299         return super.dateFromFields(fields, options);
1300       }
1302       yearMonthFromFields(fields, options) {
1303         assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
1304         return super.yearMonthFromFields(fields, options);
1305       }
1307       monthDayFromFields(fields, options) {
1308         assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
1309         return super.monthDayFromFields(fields, options);
1310       }
1312       mergeFields(fields, additionalFields) {
1313         const retval = super.mergeFields(fields, additionalFields);
1314         retval._calendar = this;
1315         retval.shouldNotBeCopied = true;
1316         return new Proxy(retval, {
1317           get(target, key) {
1318             target._calendar.mergeFieldsReturnOperations.push(`get ${key}`);
1319             const result = target[key];
1320             if (result === undefined) {
1321               return undefined;
1322             }
1323             return TemporalHelpers.toPrimitiveObserver(target._calendar.mergeFieldsReturnOperations, result, key);
1324           },
1325           has(target, key) {
1326             target._calendar.mergeFieldsReturnOperations.push(`has ${key}`);
1327             return key in target;
1328           },
1329         });
1330       }
1331     }
1332     return new CalendarMergeFieldsGetters();
1333   },
1335   /*
1336    * A custom calendar whose mergeFields() method returns a primitive value,
1337    * given by @primitive, and which records the number of calls made to its
1338    * dateFromFields(), yearMonthFromFields(), and monthDayFromFields() methods.
1339    */
1340   calendarMergeFieldsReturnsPrimitive(primitive) {
1341     class CalendarMergeFieldsPrimitive extends Temporal.Calendar {
1342       constructor(mergeFieldsReturnValue) {
1343         super("iso8601");
1344         this._mergeFieldsReturnValue = mergeFieldsReturnValue;
1345         this.dateFromFieldsCallCount = 0;
1346         this.monthDayFromFieldsCallCount = 0;
1347         this.yearMonthFromFieldsCallCount = 0;
1348       }
1350       toString() {
1351         return "merge-fields-primitive";
1352       }
1354       dateFromFields(fields, options) {
1355         this.dateFromFieldsCallCount++;
1356         return super.dateFromFields(fields, options);
1357       }
1359       yearMonthFromFields(fields, options) {
1360         this.yearMonthFromFieldsCallCount++;
1361         return super.yearMonthFromFields(fields, options);
1362       }
1364       monthDayFromFields(fields, options) {
1365         this.monthDayFromFieldsCallCount++;
1366         return super.monthDayFromFields(fields, options);
1367       }
1369       mergeFields() {
1370         return this._mergeFieldsReturnValue;
1371       }
1372     }
1373     return new CalendarMergeFieldsPrimitive(primitive);
1374   },
1376   /*
1377    * A custom calendar whose fields() method returns the same value as the
1378    * iso8601 calendar, with the addition of extraFields provided as parameter.
1379    */
1380   calendarWithExtraFields(fields) {
1381     class CalendarWithExtraFields extends Temporal.Calendar {
1382       constructor(extraFields) {
1383         super("iso8601");
1384         this._extraFields = extraFields;
1385       }
1387       fields(fieldNames) {
1388         return super.fields(fieldNames).concat(this._extraFields);
1389       }
1390     }
1392     return new CalendarWithExtraFields(fields);
1393   },
1395   /*
1396    * crossDateLineTimeZone():
1397    *
1398    * This returns an instance of a custom time zone class that implements one
1399    * single transition where the time zone moves from one side of the
1400    * International Date Line to the other, for the purpose of testing time zone
1401    * calculations without depending on system time zone data.
1402    *
1403    * The transition occurs at epoch second 1325239200 and goes from offset
1404    * -10:00 to +14:00. In other words, the time zone skips the whole calendar
1405    * day of 2011-12-30. This is the same as the real-life transition in the
1406    * Pacific/Apia time zone.
1407    */
1408   crossDateLineTimeZone() {
1409     const { compare } = Temporal.PlainDate;
1410     const skippedDay = new Temporal.PlainDate(2011, 12, 30);
1411     const transitionEpoch = 1325239200_000_000_000n;
1412     const beforeOffset = new Temporal.TimeZone("-10:00");
1413     const afterOffset = new Temporal.TimeZone("+14:00");
1415     class CrossDateLineTimeZone extends Temporal.TimeZone {
1416       constructor() {
1417         super("+14:00");
1418       }
1420       getOffsetNanosecondsFor(instant) {
1421         if (instant.epochNanoseconds < transitionEpoch) {
1422           return beforeOffset.getOffsetNanosecondsFor(instant);
1423         }
1424         return afterOffset.getOffsetNanosecondsFor(instant);
1425       }
1427       getPossibleInstantsFor(datetime) {
1428         const comparison = compare(datetime.toPlainDate(), skippedDay);
1429         if (comparison === 0) {
1430           return [];
1431         }
1432         if (comparison < 0) {
1433           return [beforeOffset.getInstantFor(datetime)];
1434         }
1435         return [afterOffset.getInstantFor(datetime)];
1436       }
1438       getPreviousTransition(instant) {
1439         if (instant.epochNanoseconds > transitionEpoch) return new Temporal.Instant(transitionEpoch);
1440         return null;
1441       }
1443       getNextTransition(instant) {
1444         if (instant.epochNanoseconds < transitionEpoch) return new Temporal.Instant(transitionEpoch);
1445         return null;
1446       }
1448       toString() {
1449         return "Custom/Date_Line";
1450       }
1451     }
1452     return new CrossDateLineTimeZone();
1453   },
1455   /*
1456    * observeProperty(calls, object, propertyName, value):
1457    *
1458    * Defines an own property @object.@propertyName with value @value, that
1459    * will log any calls to its accessors to the array @calls.
1460    */
1461   observeProperty(calls, object, propertyName, value, objectName = "") {
1462     Object.defineProperty(object, propertyName, {
1463       get() {
1464         calls.push(`get ${formatPropertyName(propertyName, objectName)}`);
1465         return value;
1466       },
1467       set(v) {
1468         calls.push(`set ${formatPropertyName(propertyName, objectName)}`);
1469       }
1470     });
1471   },
1473   /*
1474    * observeMethod(calls, object, propertyName, value):
1475    *
1476    * Defines an own property @object.@propertyName with value @value, that
1477    * will log any calls of @value to the array @calls.
1478    */
1479   observeMethod(calls, object, propertyName, objectName = "") {
1480     const method = object[propertyName];
1481     object[propertyName] = function () {
1482       calls.push(`call ${formatPropertyName(propertyName, objectName)}`);
1483       return method.apply(object, arguments);
1484     };
1485   },
1487   /*
1488    * Used for substituteMethod to indicate default behavior instead of a
1489    * substituted value
1490    */
1491   SUBSTITUTE_SKIP: SKIP_SYMBOL,
1493   /*
1494    * substituteMethod(object, propertyName, values):
1495    *
1496    * Defines an own property @object.@propertyName that will, for each
1497    * subsequent call to the method previously defined as
1498    * @object.@propertyName:
1499    *  - Call the method, if no more values remain
1500    *  - Call the method, if the value in @values for the corresponding call
1501    *    is SUBSTITUTE_SKIP
1502    *  - Otherwise, return the corresponding value in @value
1503    */
1504   substituteMethod(object, propertyName, values) {
1505     let calls = 0;
1506     const method = object[propertyName];
1507     object[propertyName] = function () {
1508       if (calls >= values.length) {
1509         return method.apply(object, arguments);
1510       } else if (values[calls] === SKIP_SYMBOL) {
1511         calls++;
1512         return method.apply(object, arguments);
1513       } else {
1514         return values[calls++];
1515       }
1516     };
1517   },
1519   /*
1520    * calendarObserver:
1521    * A custom calendar that behaves exactly like the ISO 8601 calendar but
1522    * tracks calls to any of its methods, and Get/Has operations on its
1523    * properties, by appending messages to an array. This is for the purpose of
1524    * testing order of operations that are observable from user code.
1525    * objectName is used in the log.
1526    */
1527   calendarObserver(calls, objectName, methodOverrides = {}) {
1528     function removeExtraHasPropertyChecks(objectName, calls) {
1529       // Inserting the tracking calendar into the return values of methods
1530       // that we chain up into the ISO calendar for, causes extra HasProperty
1531       // checks, which we observe. This removes them so that we don't leak
1532       // implementation details of the helper into the test code.
1533       assert.sameValue(calls.pop(), `has ${objectName}.yearOfWeek`);
1534       assert.sameValue(calls.pop(), `has ${objectName}.yearMonthFromFields`);
1535       assert.sameValue(calls.pop(), `has ${objectName}.year`);
1536       assert.sameValue(calls.pop(), `has ${objectName}.weekOfYear`);
1537       assert.sameValue(calls.pop(), `has ${objectName}.monthsInYear`);
1538       assert.sameValue(calls.pop(), `has ${objectName}.monthDayFromFields`);
1539       assert.sameValue(calls.pop(), `has ${objectName}.monthCode`);
1540       assert.sameValue(calls.pop(), `has ${objectName}.month`);
1541       assert.sameValue(calls.pop(), `has ${objectName}.mergeFields`);
1542       assert.sameValue(calls.pop(), `has ${objectName}.inLeapYear`);
1543       assert.sameValue(calls.pop(), `has ${objectName}.id`);
1544       assert.sameValue(calls.pop(), `has ${objectName}.fields`);
1545       assert.sameValue(calls.pop(), `has ${objectName}.daysInYear`);
1546       assert.sameValue(calls.pop(), `has ${objectName}.daysInWeek`);
1547       assert.sameValue(calls.pop(), `has ${objectName}.daysInMonth`);
1548       assert.sameValue(calls.pop(), `has ${objectName}.dayOfYear`);
1549       assert.sameValue(calls.pop(), `has ${objectName}.dayOfWeek`);
1550       assert.sameValue(calls.pop(), `has ${objectName}.day`);
1551       assert.sameValue(calls.pop(), `has ${objectName}.dateUntil`);
1552       assert.sameValue(calls.pop(), `has ${objectName}.dateFromFields`);
1553       assert.sameValue(calls.pop(), `has ${objectName}.dateAdd`);
1554     }
1556     const iso8601 = new Temporal.Calendar("iso8601");
1557     const trackingMethods = {
1558       dateFromFields(...args) {
1559         calls.push(`call ${objectName}.dateFromFields`);
1560         if ('dateFromFields' in methodOverrides) {
1561           const value = methodOverrides.dateFromFields;
1562           return typeof value === "function" ? value(...args) : value;
1563         }
1564         const originalResult = iso8601.dateFromFields(...args);
1565         // Replace the calendar in the result with the call-tracking calendar
1566         const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
1567         const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this);
1568         removeExtraHasPropertyChecks(objectName, calls);
1569         return result;
1570       },
1571       yearMonthFromFields(...args) {
1572         calls.push(`call ${objectName}.yearMonthFromFields`);
1573         if ('yearMonthFromFields' in methodOverrides) {
1574           const value = methodOverrides.yearMonthFromFields;
1575           return typeof value === "function" ? value(...args) : value;
1576         }
1577         const originalResult = iso8601.yearMonthFromFields(...args);
1578         // Replace the calendar in the result with the call-tracking calendar
1579         const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
1580         const result = new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
1581         removeExtraHasPropertyChecks(objectName, calls);
1582         return result;
1583       },
1584       monthDayFromFields(...args) {
1585         calls.push(`call ${objectName}.monthDayFromFields`);
1586         if ('monthDayFromFields' in methodOverrides) {
1587           const value = methodOverrides.monthDayFromFields;
1588           return typeof value === "function" ? value(...args) : value;
1589         }
1590         const originalResult = iso8601.monthDayFromFields(...args);
1591         // Replace the calendar in the result with the call-tracking calendar
1592         const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
1593         const result = new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
1594         removeExtraHasPropertyChecks(objectName, calls);
1595         return result;
1596       },
1597       dateAdd(...args) {
1598         calls.push(`call ${objectName}.dateAdd`);
1599         if ('dateAdd' in methodOverrides) {
1600           const value = methodOverrides.dateAdd;
1601           return typeof value === "function" ? value(...args) : value;
1602         }
1603         const originalResult = iso8601.dateAdd(...args);
1604         const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
1605         const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this);
1606         removeExtraHasPropertyChecks(objectName, calls);
1607         return result;
1608       },
1609       id: "iso8601",
1610     };
1611     // Automatically generate the other methods that don't need any custom code
1612     [
1613       "dateUntil",
1614       "day",
1615       "dayOfWeek",
1616       "dayOfYear",
1617       "daysInMonth",
1618       "daysInWeek",
1619       "daysInYear",
1620       "era",
1621       "eraYear",
1622       "fields",
1623       "inLeapYear",
1624       "mergeFields",
1625       "month",
1626       "monthCode",
1627       "monthsInYear",
1628       "toString",
1629       "weekOfYear",
1630       "year",
1631       "yearOfWeek",
1632     ].forEach((methodName) => {
1633       trackingMethods[methodName] = function (...args) {
1634         calls.push(`call ${formatPropertyName(methodName, objectName)}`);
1635         if (methodName in methodOverrides) {
1636           const value = methodOverrides[methodName];
1637           return typeof value === "function" ? value(...args) : value;
1638         }
1639         return iso8601[methodName](...args);
1640       };
1641     });
1642     return new Proxy(trackingMethods, {
1643       get(target, key, receiver) {
1644         const result = Reflect.get(target, key, receiver);
1645         calls.push(`get ${formatPropertyName(key, objectName)}`);
1646         return result;
1647       },
1648       has(target, key) {
1649         calls.push(`has ${formatPropertyName(key, objectName)}`);
1650         return Reflect.has(target, key);
1651       },
1652     });
1653   },
1655   /*
1656    * A custom calendar that does not allow any of its methods to be called, for
1657    * the purpose of asserting that a particular operation does not call into
1658    * user code.
1659    */
1660   calendarThrowEverything() {
1661     class CalendarThrowEverything extends Temporal.Calendar {
1662       constructor() {
1663         super("iso8601");
1664       }
1665       toString() {
1666         TemporalHelpers.assertUnreachable("toString should not be called");
1667       }
1668       dateFromFields() {
1669         TemporalHelpers.assertUnreachable("dateFromFields should not be called");
1670       }
1671       yearMonthFromFields() {
1672         TemporalHelpers.assertUnreachable("yearMonthFromFields should not be called");
1673       }
1674       monthDayFromFields() {
1675         TemporalHelpers.assertUnreachable("monthDayFromFields should not be called");
1676       }
1677       dateAdd() {
1678         TemporalHelpers.assertUnreachable("dateAdd should not be called");
1679       }
1680       dateUntil() {
1681         TemporalHelpers.assertUnreachable("dateUntil should not be called");
1682       }
1683       era() {
1684         TemporalHelpers.assertUnreachable("era should not be called");
1685       }
1686       eraYear() {
1687         TemporalHelpers.assertUnreachable("eraYear should not be called");
1688       }
1689       year() {
1690         TemporalHelpers.assertUnreachable("year should not be called");
1691       }
1692       month() {
1693         TemporalHelpers.assertUnreachable("month should not be called");
1694       }
1695       monthCode() {
1696         TemporalHelpers.assertUnreachable("monthCode should not be called");
1697       }
1698       day() {
1699         TemporalHelpers.assertUnreachable("day should not be called");
1700       }
1701       fields() {
1702         TemporalHelpers.assertUnreachable("fields should not be called");
1703       }
1704       mergeFields() {
1705         TemporalHelpers.assertUnreachable("mergeFields should not be called");
1706       }
1707     }
1709     return new CalendarThrowEverything();
1710   },
1712   /*
1713    * oneShiftTimeZone(shiftInstant, shiftNanoseconds):
1714    *
1715    * In the case of a spring-forward time zone offset transition (skipped time),
1716    * and disambiguation === 'earlier', BuiltinTimeZoneGetInstantFor subtracts a
1717    * negative number of nanoseconds from a PlainDateTime, which should balance
1718    * with the microseconds field.
1719    *
1720    * This returns an instance of a custom time zone class which skips a length
1721    * of time equal to shiftNanoseconds (a number), at the Temporal.Instant
1722    * shiftInstant. Before shiftInstant, it's identical to UTC, and after
1723    * shiftInstant it's a constant-offset time zone.
1724    *
1725    * It provides a getPossibleInstantsForCalledWith member which is an array
1726    * with the result of calling toString() on any PlainDateTimes passed to
1727    * getPossibleInstantsFor().
1728    */
1729   oneShiftTimeZone(shiftInstant, shiftNanoseconds) {
1730     class OneShiftTimeZone extends Temporal.TimeZone {
1731       constructor(shiftInstant, shiftNanoseconds) {
1732         super("+00:00");
1733         this._shiftInstant = shiftInstant;
1734         this._epoch1 = shiftInstant.epochNanoseconds;
1735         this._epoch2 = this._epoch1 + BigInt(shiftNanoseconds);
1736         this._shiftNanoseconds = shiftNanoseconds;
1737         this._shift = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, this._shiftNanoseconds);
1738         this.getPossibleInstantsForCalledWith = [];
1739       }
1741       _isBeforeShift(instant) {
1742         return instant.epochNanoseconds < this._epoch1;
1743       }
1745       getOffsetNanosecondsFor(instant) {
1746         return this._isBeforeShift(instant) ? 0 : this._shiftNanoseconds;
1747       }
1749       getPossibleInstantsFor(plainDateTime) {
1750         this.getPossibleInstantsForCalledWith.push(plainDateTime.toString({ calendarName: "never" }));
1751         const [instant] = super.getPossibleInstantsFor(plainDateTime);
1752         if (this._shiftNanoseconds > 0) {
1753           if (this._isBeforeShift(instant)) return [instant];
1754           if (instant.epochNanoseconds < this._epoch2) return [];
1755           return [instant.subtract(this._shift)];
1756         }
1757         if (instant.epochNanoseconds < this._epoch2) return [instant];
1758         const shifted = instant.subtract(this._shift);
1759         if (this._isBeforeShift(instant)) return [instant, shifted];
1760         return [shifted];
1761       }
1763       getNextTransition(instant) {
1764         return this._isBeforeShift(instant) ? this._shiftInstant : null;
1765       }
1767       getPreviousTransition(instant) {
1768         return this._isBeforeShift(instant) ? null : this._shiftInstant;
1769       }
1771       toString() {
1772         return "Custom/One_Shift";
1773       }
1774     }
1775     return new OneShiftTimeZone(shiftInstant, shiftNanoseconds);
1776   },
1778   /*
1779    * propertyBagObserver():
1780    * Returns an object that behaves like the given propertyBag but tracks Get
1781    * and Has operations on any of its properties, by appending messages to an
1782    * array. If the value of a property in propertyBag is a primitive, the value
1783    * of the returned object's property will additionally be a
1784    * TemporalHelpers.toPrimitiveObserver that will track calls to its toString
1785    * and valueOf methods in the same array. This is for the purpose of testing
1786    * order of operations that are observable from user code. objectName is used
1787    * in the log.
1788    */
1789   propertyBagObserver(calls, propertyBag, objectName) {
1790     return new Proxy(propertyBag, {
1791       ownKeys(target) {
1792         calls.push(`ownKeys ${objectName}`);
1793         return Reflect.ownKeys(target);
1794       },
1795       getOwnPropertyDescriptor(target, key) {
1796         calls.push(`getOwnPropertyDescriptor ${formatPropertyName(key, objectName)}`);
1797         return Reflect.getOwnPropertyDescriptor(target, key);
1798       },
1799       get(target, key, receiver) {
1800         calls.push(`get ${formatPropertyName(key, objectName)}`);
1801         const result = Reflect.get(target, key, receiver);
1802         if (result === undefined) {
1803           return undefined;
1804         }
1805         if ((result !== null && typeof result === "object") || typeof result === "function") {
1806           return result;
1807         }
1808         return TemporalHelpers.toPrimitiveObserver(calls, result, `${formatPropertyName(key, objectName)}`);
1809       },
1810       has(target, key) {
1811         calls.push(`has ${formatPropertyName(key, objectName)}`);
1812         return Reflect.has(target, key);
1813       },
1814     });
1815   },
1817   /*
1818    * specificOffsetTimeZone():
1819    *
1820    * This returns an instance of a custom time zone class, which returns a
1821    * specific custom value from its getOffsetNanosecondsFrom() method. This is
1822    * for the purpose of testing the validation of what this method returns.
1823    *
1824    * It also returns an empty array from getPossibleInstantsFor(), so as to
1825    * trigger calls to getOffsetNanosecondsFor() when used from the
1826    * BuiltinTimeZoneGetInstantFor operation.
1827    */
1828   specificOffsetTimeZone(offsetValue) {
1829     class SpecificOffsetTimeZone extends Temporal.TimeZone {
1830       constructor(offsetValue) {
1831         super("UTC");
1832         this._offsetValue = offsetValue;
1833       }
1835       getOffsetNanosecondsFor() {
1836         return this._offsetValue;
1837       }
1839       getPossibleInstantsFor(dt) {
1840         if (typeof this._offsetValue !== 'number' || Math.abs(this._offsetValue) >= 86400e9 || isNaN(this._offsetValue)) return [];
1841         const zdt = dt.toZonedDateTime("UTC").add({ nanoseconds: -this._offsetValue });
1842         return [zdt.toInstant()];
1843       }
1845       get id() {
1846         return this.getOffsetStringFor(new Temporal.Instant(0n));
1847       }
1848     }
1849     return new SpecificOffsetTimeZone(offsetValue);
1850   },
1852   /*
1853    * springForwardFallBackTimeZone():
1854    *
1855    * This returns an instance of a custom time zone class that implements one
1856    * single spring-forward/fall-back transition, for the purpose of testing the
1857    * disambiguation option, without depending on system time zone data.
1858    *
1859    * The spring-forward occurs at epoch second 954669600 (2000-04-02T02:00
1860    * local) and goes from offset -08:00 to -07:00.
1861    *
1862    * The fall-back occurs at epoch second 972810000 (2000-10-29T02:00 local) and
1863    * goes from offset -07:00 to -08:00.
1864    */
1865   springForwardFallBackTimeZone() {
1866     const { compare } = Temporal.PlainDateTime;
1867     const springForwardLocal = new Temporal.PlainDateTime(2000, 4, 2, 2);
1868     const springForwardEpoch = 954669600_000_000_000n;
1869     const fallBackLocal = new Temporal.PlainDateTime(2000, 10, 29, 1);
1870     const fallBackEpoch = 972810000_000_000_000n;
1871     const winterOffset = new Temporal.TimeZone('-08:00');
1872     const summerOffset = new Temporal.TimeZone('-07:00');
1874     class SpringForwardFallBackTimeZone extends Temporal.TimeZone {
1875       constructor() {
1876         super("-08:00");
1877       }
1879       getOffsetNanosecondsFor(instant) {
1880         if (instant.epochNanoseconds < springForwardEpoch ||
1881           instant.epochNanoseconds >= fallBackEpoch) {
1882           return winterOffset.getOffsetNanosecondsFor(instant);
1883         }
1884         return summerOffset.getOffsetNanosecondsFor(instant);
1885       }
1887       getPossibleInstantsFor(datetime) {
1888         if (compare(datetime, springForwardLocal) >= 0 && compare(datetime, springForwardLocal.add({ hours: 1 })) < 0) {
1889           return [];
1890         }
1891         if (compare(datetime, fallBackLocal) >= 0 && compare(datetime, fallBackLocal.add({ hours: 1 })) < 0) {
1892           return [summerOffset.getInstantFor(datetime), winterOffset.getInstantFor(datetime)];
1893         }
1894         if (compare(datetime, springForwardLocal) < 0 || compare(datetime, fallBackLocal) >= 0) {
1895           return [winterOffset.getInstantFor(datetime)];
1896         }
1897         return [summerOffset.getInstantFor(datetime)];
1898       }
1900       getPreviousTransition(instant) {
1901         if (instant.epochNanoseconds > fallBackEpoch) return new Temporal.Instant(fallBackEpoch);
1902         if (instant.epochNanoseconds > springForwardEpoch) return new Temporal.Instant(springForwardEpoch);
1903         return null;
1904       }
1906       getNextTransition(instant) {
1907         if (instant.epochNanoseconds < springForwardEpoch) return new Temporal.Instant(springForwardEpoch);
1908         if (instant.epochNanoseconds < fallBackEpoch) return new Temporal.Instant(fallBackEpoch);
1909         return null;
1910       }
1912       get id() {
1913         return "Custom/Spring_Fall";
1914       }
1916       toString() {
1917         return "Custom/Spring_Fall";
1918       }
1919     }
1920     return new SpringForwardFallBackTimeZone();
1921   },
1923   /*
1924    * timeZoneObserver:
1925    * A custom calendar that behaves exactly like the UTC time zone but tracks
1926    * calls to any of its methods, and Get/Has operations on its properties, by
1927    * appending messages to an array. This is for the purpose of testing order of
1928    * operations that are observable from user code. objectName is used in the
1929    * log. methodOverrides is an optional object containing properties with the
1930    * same name as Temporal.TimeZone methods. If the property value is a function
1931    * it will be called with the proper arguments instead of the UTC method.
1932    * Otherwise, the property value will be returned directly.
1933    */
1934   timeZoneObserver(calls, objectName, methodOverrides = {}) {
1935     const utc = new Temporal.TimeZone("UTC");
1936     const trackingMethods = {
1937       id: "UTC",
1938     };
1939     // Automatically generate the methods
1940     ["getOffsetNanosecondsFor", "getPossibleInstantsFor", "toString"].forEach((methodName) => {
1941       trackingMethods[methodName] = function (...args) {
1942         calls.push(`call ${formatPropertyName(methodName, objectName)}`);
1943         if (methodName in methodOverrides) {
1944           const value = methodOverrides[methodName];
1945           return typeof value === "function" ? value(...args) : value;
1946         }
1947         return utc[methodName](...args);
1948       };
1949     });
1950     return new Proxy(trackingMethods, {
1951       get(target, key, receiver) {
1952         const result = Reflect.get(target, key, receiver);
1953         calls.push(`get ${formatPropertyName(key, objectName)}`);
1954         return result;
1955       },
1956       has(target, key) {
1957         calls.push(`has ${formatPropertyName(key, objectName)}`);
1958         return Reflect.has(target, key);
1959       },
1960     });
1961   },
1963   /*
1964    * A custom time zone that does not allow any of its methods to be called, for
1965    * the purpose of asserting that a particular operation does not call into
1966    * user code.
1967    */
1968   timeZoneThrowEverything() {
1969     class TimeZoneThrowEverything extends Temporal.TimeZone {
1970       constructor() {
1971         super("UTC");
1972       }
1973       getOffsetNanosecondsFor() {
1974         TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be called");
1975       }
1976       getPossibleInstantsFor() {
1977         TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be called");
1978       }
1979       toString() {
1980         TemporalHelpers.assertUnreachable("toString should not be called");
1981       }
1982     }
1984     return new TimeZoneThrowEverything();
1985   },
1987   /*
1988    * Returns an object that will append logs of any Gets or Calls of its valueOf
1989    * or toString properties to the array calls. Both valueOf and toString will
1990    * return the actual primitiveValue. propertyName is used in the log.
1991    */
1992   toPrimitiveObserver(calls, primitiveValue, propertyName) {
1993     return {
1994       get valueOf() {
1995         calls.push(`get ${propertyName}.valueOf`);
1996         return function () {
1997           calls.push(`call ${propertyName}.valueOf`);
1998           return primitiveValue;
1999         };
2000       },
2001       get toString() {
2002         calls.push(`get ${propertyName}.toString`);
2003         return function () {
2004           calls.push(`call ${propertyName}.toString`);
2005           if (primitiveValue === undefined) return undefined;
2006           return primitiveValue.toString();
2007         };
2008       },
2009     };
2010   },
2012   /*
2013    * An object containing further methods that return arrays of ISO strings, for
2014    * testing parsers.
2015    */
2016   ISO: {
2017     /*
2018      * PlainMonthDay strings that are not valid.
2019      */
2020     plainMonthDayStringsInvalid() {
2021       return [
2022         "11-18junk",
2023         "11-18[u-ca=gregory]",
2024         "11-18[u-ca=hebrew]",
2025         "11-18[U-CA=iso8601]",
2026         "11-18[u-CA=iso8601]",
2027         "11-18[FOO=bar]",
2028       ];
2029     },
2031     /*
2032      * PlainMonthDay strings that are valid and that should produce October 1st.
2033      */
2034     plainMonthDayStringsValid() {
2035       return [
2036         "10-01",
2037         "1001",
2038         "1965-10-01",
2039         "1976-10-01T152330.1+00:00",
2040         "19761001T15:23:30.1+00:00",
2041         "1976-10-01T15:23:30.1+0000",
2042         "1976-10-01T152330.1+0000",
2043         "19761001T15:23:30.1+0000",
2044         "19761001T152330.1+00:00",
2045         "19761001T152330.1+0000",
2046         "+001976-10-01T152330.1+00:00",
2047         "+0019761001T15:23:30.1+00:00",
2048         "+001976-10-01T15:23:30.1+0000",
2049         "+001976-10-01T152330.1+0000",
2050         "+0019761001T15:23:30.1+0000",
2051         "+0019761001T152330.1+00:00",
2052         "+0019761001T152330.1+0000",
2053         "1976-10-01T15:23:00",
2054         "1976-10-01T15:23",
2055         "1976-10-01T15",
2056         "1976-10-01",
2057         "--10-01",
2058         "--1001",
2059       ];
2060     },
2062     /*
2063      * PlainTime strings that may be mistaken for PlainMonthDay or
2064      * PlainYearMonth strings, and so require a time designator.
2065      */
2066     plainTimeStringsAmbiguous() {
2067       const ambiguousStrings = [
2068         "2021-12",  // ambiguity between YYYY-MM and HHMM-UU
2069         "2021-12[-12:00]",  // ditto, TZ does not disambiguate
2070         "1214",     // ambiguity between MMDD and HHMM
2071         "0229",     //   ditto, including MMDD that doesn't occur every year
2072         "1130",     //   ditto, including DD that doesn't occur in every month
2073         "12-14",    // ambiguity between MM-DD and HH-UU
2074         "12-14[-14:00]",  // ditto, TZ does not disambiguate
2075         "202112",   // ambiguity between YYYYMM and HHMMSS
2076         "202112[UTC]",  // ditto, TZ does not disambiguate
2077       ];
2078       // Adding a calendar annotation to one of these strings must not cause
2079       // disambiguation in favour of time.
2080       const stringsWithCalendar = ambiguousStrings.map((s) => s + '[u-ca=iso8601]');
2081       return ambiguousStrings.concat(stringsWithCalendar);
2082     },
2084     /*
2085      * PlainTime strings that are of similar form to PlainMonthDay and
2086      * PlainYearMonth strings, but are not ambiguous due to components that
2087      * aren't valid as months or days.
2088      */
2089     plainTimeStringsUnambiguous() {
2090       return [
2091         "2021-13",          // 13 is not a month
2092         "202113",           //   ditto
2093         "2021-13[-13:00]",  //   ditto
2094         "202113[-13:00]",   //   ditto
2095         "0000-00",          // 0 is not a month
2096         "000000",           //   ditto
2097         "0000-00[UTC]",     //   ditto
2098         "000000[UTC]",      //   ditto
2099         "1314",             // 13 is not a month
2100         "13-14",            //   ditto
2101         "1232",             // 32 is not a day
2102         "0230",             // 30 is not a day in February
2103         "0631",             // 31 is not a day in June
2104         "0000",             // 0 is neither a month nor a day
2105         "00-00",            //   ditto
2106       ];
2107     },
2109     /*
2110      * PlainYearMonth-like strings that are not valid.
2111      */
2112     plainYearMonthStringsInvalid() {
2113       return [
2114         "2020-13",
2115         "1976-11[u-ca=gregory]",
2116         "1976-11[u-ca=hebrew]",
2117         "1976-11[U-CA=iso8601]",
2118         "1976-11[u-CA=iso8601]",
2119         "1976-11[FOO=bar]",
2120       ];
2121     },
2123     /*
2124      * PlainYearMonth-like strings that are valid and should produce November
2125      * 1976 in the ISO 8601 calendar.
2126      */
2127     plainYearMonthStringsValid() {
2128       return [
2129         "1976-11",
2130         "1976-11-10",
2131         "1976-11-01T09:00:00+00:00",
2132         "1976-11-01T00:00:00+05:00",
2133         "197611",
2134         "+00197611",
2135         "1976-11-18T15:23:30.1\u221202:00",
2136         "1976-11-18T152330.1+00:00",
2137         "19761118T15:23:30.1+00:00",
2138         "1976-11-18T15:23:30.1+0000",
2139         "1976-11-18T152330.1+0000",
2140         "19761118T15:23:30.1+0000",
2141         "19761118T152330.1+00:00",
2142         "19761118T152330.1+0000",
2143         "+001976-11-18T152330.1+00:00",
2144         "+0019761118T15:23:30.1+00:00",
2145         "+001976-11-18T15:23:30.1+0000",
2146         "+001976-11-18T152330.1+0000",
2147         "+0019761118T15:23:30.1+0000",
2148         "+0019761118T152330.1+00:00",
2149         "+0019761118T152330.1+0000",
2150         "1976-11-18T15:23",
2151         "1976-11-18T15",
2152         "1976-11-18",
2153       ];
2154     },
2156     /*
2157      * PlainYearMonth-like strings that are valid and should produce November of
2158      * the ISO year -9999.
2159      */
2160     plainYearMonthStringsValidNegativeYear() {
2161       return [
2162         "\u2212009999-11",
2163       ];
2164     },
2165   }