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.
7 This defines helper objects and functions for testing Temporal.
8 defines: [TemporalHelpers]
9 features: [Symbol.species, Symbol.iterator, Temporal]
12 const ASCII_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/u;
14 function formatPropertyName(propertyKey, objectName = "") {
15 switch (typeof propertyKey) {
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}]`;
22 return `${objectName}[Symbol('${propertyKey.description}')]`
25 if (propertyKey !== String(Number(propertyKey))) {
26 if (ASCII_IDENTIFIER.test(propertyKey)) {
27 return objectName ? `${objectName}.${propertyKey}` : propertyKey;
29 return `${objectName}['${propertyKey.replace(/'/g, "\\'")}']`
33 // integer or string integer-index
34 return `${objectName}[${propertyKey}]`;
38 const SKIP_SYMBOL = Symbol("Skip");
40 var TemporalHelpers = {
42 * Codes and maximum lengths of months in the ISO 8601 calendar.
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 }
60 * assertDuration(duration, years, ..., nanoseconds[, description]):
62 * Shorthand for asserting that each field of a Temporal.Duration is equal to
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`);
81 * assertDateDuration(duration, years, months, weeks, days, [, description]):
83 * Shorthand for asserting that each date field of a Temporal.Duration is
84 * equal to an expected value.
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:`);
102 * assertDurationsEqual(actual, expected[, description]):
104 * Shorthand for asserting that each field of a Temporal.Duration is equal to
105 * the corresponding field in another Temporal.Duration.
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);
114 * assertInstantsEqual(actual, expected[, description]):
116 * Shorthand for asserting that two Temporal.Instants are of the correct type
117 * and equal according to their equals() methods.
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`);
127 * assertPlainDate(date, year, ..., nanosecond[, description[, era, eraYear]]):
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.)
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:`);
146 * assertPlainDateTime(datetime, year, ..., nanosecond[, description[, era, eraYear]]):
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.)
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:`);
171 * assertPlainDateTimesEqual(actual, expected[, description]):
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.
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`);
183 actual.getISOFields().calendar,
184 expected.getISOFields().calendar,
185 `${prefix}calendar same value:`
190 * assertPlainMonthDay(monthDay, monthCode, day[, description [, referenceISOYear]]):
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().)
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:`);
206 * assertPlainTime(time, hour, ..., nanosecond[, description]):
208 * Shorthand for asserting that each field of a Temporal.PlainTime is equal to
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:`);
223 * assertPlainTimesEqual(actual, expected[, description]):
225 * Shorthand for asserting that two Temporal.PlainTimes are of the correct
226 * type and equal according to their equals() methods.
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`);
236 * assertPlainYearMonth(yearMonth, year, month, monthCode[, description[, era, eraYear, referenceISODay]]):
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.)
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:`);
255 * assertZonedDateTimesEqual(actual, expected[, description]):
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.
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:`);
268 actual.getISOFields().calendar,
269 expected.getISOFields().calendar,
270 `${prefix}calendar same value:`
275 * assertUnreachable(description):
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.
280 assertUnreachable(description) {
281 let message = "This code should not be executed";
283 message = `${message}: ${description}`;
285 throw new Test262Error(message);
289 * checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls):
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.)
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.
304 checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls) {
307 class DateUntilOptionsCalendar extends Temporal.Calendar {
312 dateUntil(earlier, later, options) {
313 actual.push(options.largestUnit);
314 return super.dateUntil(earlier, later, options);
318 return "date-until-options";
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
331 * checkPlainDateTimeConversionFastPath(func):
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.
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.)
343 checkPlainDateTimeConversionFastPath(func, message = "checkPlainDateTimeConversionFastPath") {
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, {
353 actual.push(`get ${formatPropertyName(property)}`);
354 const value = prototypeDescrs[property].get.call(this);
357 actual.push(`toString ${formatPropertyName(property)}`);
358 return value.toString();
361 actual.push(`valueOf ${formatPropertyName(property)}`);
368 Object.defineProperty(datetime, "calendar", {
370 actual.push("get calendar");
375 func(datetime, calendar);
376 assert.compareArray(actual, expected, `${message}: property getters not called`);
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
388 checkPluralUnitsAccepted(func, validSingularUnits) {
397 millisecond: 'milliseconds',
398 microsecond: 'microseconds',
399 nanosecond: 'nanoseconds',
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);
417 assert.sameValue(pluralValue, singularValue);
423 * checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc):
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()
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");
449 "get roundingIncrement.valueOf",
450 "call roundingIncrement.valueOf",
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");
460 * checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc):
462 * Checks the type handling of a string option, of which there are several in
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.
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");
489 `get ${propertyName}.toString`,
490 `call ${propertyName}.toString`,
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");
500 * checkSubclassingIgnored(construct, constructArgs, method, methodArgs,
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.
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.
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);
527 * Checks that replacing the 'constructor' property of the instance with
528 * various primitive values does not affect the returned new instance.
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);
541 check("test", "string");
542 check(Symbol(), "Symbol");
548 * Checks that replacing the 'constructor' property of the subclass with
549 * undefined does not affect the returned new instance.
551 checkSubclassConstructorUndefined(construct, constructArgs, method, methodArgs, resultAssertions) {
554 class MySubclass extends construct {
557 super(...constructArgs);
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);
573 * Checks that making the 'constructor' property of the instance throw when
574 * called does not affect the returned new instance.
576 checkSubclassConstructorThrows(construct, constructArgs, method, methodArgs, resultAssertions) {
577 function CustomError() {}
578 const instance = new construct(...constructArgs);
579 Object.defineProperty(instance, "constructor", {
581 throw new CustomError();
584 const result = instance[method](...methodArgs);
585 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
586 resultAssertions(result);
590 * Checks that when subclassing, the subclass constructor is not called by
591 * the method under test.
593 checkSubclassConstructorNotCalled(construct, constructArgs, method, methodArgs, resultAssertions) {
596 class MySubclass extends construct {
599 super(...constructArgs);
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);
613 * Check that the constructor's @@species property is ignored when it's a
614 * constructor that returns a non-object value.
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() {
624 const result = instance[method](...methodArgs);
625 assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
626 resultAssertions(result);
629 check(undefined, "undefined");
632 check("test", "string");
633 check(Symbol(), "Symbol");
636 check({}, "plain object");
640 * Check that the constructor's @@species property is ignored when it's not a
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,
649 const result = instance[method](...methodArgs);
650 assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
651 resultAssertions(result);
655 check("test", "string");
656 check(Symbol(), "Symbol");
659 check({}, "plain object");
663 * Check that the constructor's @@species property is ignored when it's null.
665 checkSubclassSpeciesNull(construct, constructArgs, method, methodArgs, resultAssertions) {
668 class MySubclass extends construct {
671 super(...constructArgs);
675 const instance = new MySubclass();
676 assert.sameValue(called, 1);
678 MySubclass.prototype.constructor = {
679 [Symbol.species]: null,
682 const result = instance[method](...methodArgs);
683 assert.sameValue(called, 1);
684 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
685 resultAssertions(result);
689 * Check that the constructor's @@species property is ignored when it's
692 checkSubclassSpeciesUndefined(construct, constructArgs, method, methodArgs, resultAssertions) {
695 class MySubclass extends construct {
698 super(...constructArgs);
702 const instance = new MySubclass();
703 assert.sameValue(called, 1);
705 MySubclass.prototype.constructor = {
706 [Symbol.species]: undefined,
709 const result = instance[method](...methodArgs);
710 assert.sameValue(called, 1);
711 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
712 resultAssertions(result);
716 * Check that the constructor's @@species property is ignored when it throws,
717 * i.e. it is not called at all.
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();
729 const result = instance[method](...methodArgs);
730 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
734 * checkSubclassingIgnoredStatic(construct, method, methodArgs, resultAssertions):
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
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.
746 checkSubclassingIgnoredStatic(...args) {
747 this.checkStaticInvalidReceiver(...args);
748 this.checkStaticReceiverNotCalled(...args);
749 this.checkThisValueNotCalled(...args);
753 * Check that calling the static method with a receiver that's not callable,
754 * still calls the intrinsic constructor.
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);
763 check(undefined, "undefined");
766 check("test", "string");
767 check(Symbol(), "symbol");
770 check({}, "Non-callable object");
774 * Check that calling the static method with a receiver that returns a value
775 * that's not callable, still calls the intrinsic constructor.
777 checkStaticReceiverNotCalled(construct, method, methodArgs, resultAssertions) {
778 function check(value, description) {
779 const receiver = function () {
782 const result = construct[method].apply(receiver, 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 the receiver isn't called.
800 checkThisValueNotCalled(construct, method, methodArgs, resultAssertions) {
803 class MySubclass extends construct {
804 constructor(...args) {
810 const result = MySubclass[method](...methodArgs);
811 assert.sameValue(called, false);
812 assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
813 resultAssertions(result);
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.
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 {
832 this.getPossibleInstantsForCallCount = 0;
833 this.getPossibleInstantsForCalledWith = [];
834 this.getPossibleInstantsForReturns = [];
835 this.iteratorExhausted = [];
839 return "Custom/Iterable";
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;
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) {
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 });
865 this.getPossibleInstantsForReturns.push(retval);
866 this.iteratorExhausted.push(false);
868 callIndex: this.getPossibleInstantsForCallCount - 1,
870 *[Symbol.iterator]() {
871 yield* this.timeZone.getPossibleInstantsForReturns[this.callIndex];
872 this.timeZone.iteratorExhausted[this.callIndex] = true;
878 const timeZone = new TimeZonePossibleInstantsIterable();
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");
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.)
898 checkToTemporalCalendarFastPath(func) {
899 class CalendarFastPathCheck extends Temporal.Calendar {
904 dateFromFields(...args) {
905 return super.dateFromFields(...args).withCalendar(this);
908 monthDayFromFields(...args) {
909 const { isoYear, isoMonth, isoDay } = super.monthDayFromFields(...args).getISOFields();
910 return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
913 yearMonthFromFields(...args) {
914 const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields(...args).getISOFields();
915 return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
919 return "fast-path-check";
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) => {
934 Object.defineProperty(temporalObject, "calendar", {
936 actual.push("get calendar");
941 func(temporalObject, calendar);
942 assert.compareArray(actual, expected, "calendar getter not called");
946 checkToTemporalInstantFastPath(func) {
950 const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
951 Object.defineProperty(datetime, 'toString', {
953 actual.push("get toString");
954 return function (options) {
955 actual.push("call toString");
956 return Temporal.ZonedDateTime.prototype.toString.call(this, options);
962 assert.compareArray(actual, expected, "toString not called");
965 checkToTemporalPlainDateTimeFastPath(func) {
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, {
975 actual.push(`get ${formatPropertyName(property)}`);
976 const value = prototypeDescrs[property].get.call(this);
977 return TemporalHelpers.toPrimitiveObserver(actual, value, property);
981 ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => {
982 Object.defineProperty(date, property, {
984 actual.push(`get ${formatPropertyName(property)}`);
989 Object.defineProperty(date, "calendar", {
991 actual.push("get calendar");
996 func(date, calendar);
997 assert.compareArray(actual, expected, "property getters not called");
1001 * A custom calendar used in prototype pollution checks. Verifies that the
1002 * fromFields methods are always called with a null-prototype fields object.
1004 calendarCheckFieldsPrototypePollution() {
1005 class CalendarCheckFieldsPrototypePollution extends Temporal.Calendar {
1008 this.dateFromFieldsCallCount = 0;
1009 this.yearMonthFromFieldsCallCount = 0;
1010 this.monthDayFromFieldsCallCount = 0;
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);
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);
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);
1035 return new CalendarCheckFieldsPrototypePollution();
1039 * A custom calendar used in prototype pollution checks. Verifies that the
1040 * mergeFields() method is always called with null-prototype fields objects.
1042 calendarCheckMergeFieldsPrototypePollution() {
1043 class CalendarCheckMergeFieldsPrototypePollution extends Temporal.Calendar {
1046 this.mergeFieldsCallCount = 0;
1050 return "merge-fields-null-proto";
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);
1061 return new CalendarCheckMergeFieldsPrototypePollution();
1065 * A custom calendar used in prototype pollution checks. Verifies that methods
1066 * are always called with a null-prototype options object.
1068 calendarCheckOptionsPrototypePollution() {
1069 class CalendarCheckOptionsPrototypePollution extends Temporal.Calendar {
1072 this.yearMonthFromFieldsCallCount = 0;
1073 this.dateUntilCallCount = 0;
1077 return "options-null-proto";
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);
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);
1093 return new CalendarCheckOptionsPrototypePollution();
1097 * A custom calendar that asserts its dateAdd() method is called with the
1098 * options parameter having the value undefined.
1100 calendarDateAddUndefinedOptions() {
1101 class CalendarDateAddUndefinedOptions extends Temporal.Calendar {
1104 this.dateAddCallCount = 0;
1108 return "dateadd-undef-options";
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);
1117 return new CalendarDateAddUndefinedOptions();
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
1126 calendarDateAddPlainDateInstance() {
1127 class CalendarDateAddPlainDateInstance extends Temporal.Calendar {
1130 this.dateAddCallCount = 0;
1131 this.specificPlainDate = undefined;
1135 return "dateadd-plain-date-instance";
1138 dateFromFields(...args) {
1139 return super.dateFromFields(...args).withCalendar(this);
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}`);
1148 return super.dateAdd(date, duration, options).withCalendar(this);
1151 return new CalendarDateAddPlainDateInstance();
1155 * A custom calendar that returns an iterable instead of an array from its
1156 * fields() method, otherwise identical to the ISO calendar.
1158 calendarFieldsIterable() {
1159 class CalendarFieldsIterable extends Temporal.Calendar {
1162 this.fieldsCallCount = 0;
1163 this.fieldsCalledWith = [];
1164 this.iteratorExhausted = [];
1168 return "fields-iterable";
1171 fields(fieldNames) {
1172 this.fieldsCallCount++;
1173 this.fieldsCalledWith.push(fieldNames.slice());
1174 this.iteratorExhausted.push(false);
1176 callIndex: this.fieldsCallCount - 1,
1178 *[Symbol.iterator]() {
1179 yield* this.calendar.fieldsCalledWith[this.callIndex];
1180 this.calendar.iteratorExhausted[this.callIndex] = true;
1185 return new CalendarFieldsIterable();
1189 * A custom calendar that asserts its ...FromFields() methods are called with
1190 * the options parameter having the value undefined.
1192 calendarFromFieldsUndefinedOptions() {
1193 class CalendarFromFieldsUndefinedOptions extends Temporal.Calendar {
1196 this.dateFromFieldsCallCount = 0;
1197 this.monthDayFromFieldsCallCount = 0;
1198 this.yearMonthFromFieldsCallCount = 0;
1202 return "from-fields-undef-options";
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);
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);
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);
1223 return new CalendarFromFieldsUndefinedOptions();
1227 * A custom calendar that modifies the fields object passed in to
1228 * dateFromFields, sabotaging its time properties.
1230 calendarMakeInfinityTime() {
1231 class CalendarMakeInfinityTime extends Temporal.Calendar {
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;
1247 return new CalendarMakeInfinityTime();
1251 * A custom calendar that defines getters on the fields object passed into
1252 * dateFromFields that throw, sabotaging its time properties.
1254 calendarMakeInvalidGettersTime() {
1255 class CalendarMakeInvalidGettersTime extends Temporal.Calendar {
1260 dateFromFields(fields, options) {
1261 const retval = super.dateFromFields(fields, options);
1262 const throwingDescriptor = {
1264 throw new Test262Error("reading a sabotaged time field");
1267 Object.defineProperties(fields, {
1268 hour: throwingDescriptor,
1269 minute: throwingDescriptor,
1270 second: throwingDescriptor,
1271 millisecond: throwingDescriptor,
1272 microsecond: throwingDescriptor,
1273 nanosecond: throwingDescriptor,
1278 return new CalendarMakeInvalidGettersTime();
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.
1286 calendarMergeFieldsGetters() {
1287 class CalendarMergeFieldsGetters extends Temporal.Calendar {
1290 this.mergeFieldsReturnOperations = [];
1294 return "merge-fields-getters";
1297 dateFromFields(fields, options) {
1298 assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
1299 return super.dateFromFields(fields, options);
1302 yearMonthFromFields(fields, options) {
1303 assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
1304 return super.yearMonthFromFields(fields, options);
1307 monthDayFromFields(fields, options) {
1308 assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
1309 return super.monthDayFromFields(fields, options);
1312 mergeFields(fields, additionalFields) {
1313 const retval = super.mergeFields(fields, additionalFields);
1314 retval._calendar = this;
1315 retval.shouldNotBeCopied = true;
1316 return new Proxy(retval, {
1318 target._calendar.mergeFieldsReturnOperations.push(`get ${key}`);
1319 const result = target[key];
1320 if (result === undefined) {
1323 return TemporalHelpers.toPrimitiveObserver(target._calendar.mergeFieldsReturnOperations, result, key);
1326 target._calendar.mergeFieldsReturnOperations.push(`has ${key}`);
1327 return key in target;
1332 return new CalendarMergeFieldsGetters();
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.
1340 calendarMergeFieldsReturnsPrimitive(primitive) {
1341 class CalendarMergeFieldsPrimitive extends Temporal.Calendar {
1342 constructor(mergeFieldsReturnValue) {
1344 this._mergeFieldsReturnValue = mergeFieldsReturnValue;
1345 this.dateFromFieldsCallCount = 0;
1346 this.monthDayFromFieldsCallCount = 0;
1347 this.yearMonthFromFieldsCallCount = 0;
1351 return "merge-fields-primitive";
1354 dateFromFields(fields, options) {
1355 this.dateFromFieldsCallCount++;
1356 return super.dateFromFields(fields, options);
1359 yearMonthFromFields(fields, options) {
1360 this.yearMonthFromFieldsCallCount++;
1361 return super.yearMonthFromFields(fields, options);
1364 monthDayFromFields(fields, options) {
1365 this.monthDayFromFieldsCallCount++;
1366 return super.monthDayFromFields(fields, options);
1370 return this._mergeFieldsReturnValue;
1373 return new CalendarMergeFieldsPrimitive(primitive);
1377 * A custom calendar whose fields() method returns the same value as the
1378 * iso8601 calendar, with the addition of extraFields provided as parameter.
1380 calendarWithExtraFields(fields) {
1381 class CalendarWithExtraFields extends Temporal.Calendar {
1382 constructor(extraFields) {
1384 this._extraFields = extraFields;
1387 fields(fieldNames) {
1388 return super.fields(fieldNames).concat(this._extraFields);
1392 return new CalendarWithExtraFields(fields);
1396 * crossDateLineTimeZone():
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.
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.
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 {
1420 getOffsetNanosecondsFor(instant) {
1421 if (instant.epochNanoseconds < transitionEpoch) {
1422 return beforeOffset.getOffsetNanosecondsFor(instant);
1424 return afterOffset.getOffsetNanosecondsFor(instant);
1427 getPossibleInstantsFor(datetime) {
1428 const comparison = compare(datetime.toPlainDate(), skippedDay);
1429 if (comparison === 0) {
1432 if (comparison < 0) {
1433 return [beforeOffset.getInstantFor(datetime)];
1435 return [afterOffset.getInstantFor(datetime)];
1438 getPreviousTransition(instant) {
1439 if (instant.epochNanoseconds > transitionEpoch) return new Temporal.Instant(transitionEpoch);
1443 getNextTransition(instant) {
1444 if (instant.epochNanoseconds < transitionEpoch) return new Temporal.Instant(transitionEpoch);
1449 return "Custom/Date_Line";
1452 return new CrossDateLineTimeZone();
1456 * observeProperty(calls, object, propertyName, value):
1458 * Defines an own property @object.@propertyName with value @value, that
1459 * will log any calls to its accessors to the array @calls.
1461 observeProperty(calls, object, propertyName, value, objectName = "") {
1462 Object.defineProperty(object, propertyName, {
1464 calls.push(`get ${formatPropertyName(propertyName, objectName)}`);
1468 calls.push(`set ${formatPropertyName(propertyName, objectName)}`);
1474 * observeMethod(calls, object, propertyName, value):
1476 * Defines an own property @object.@propertyName with value @value, that
1477 * will log any calls of @value to the array @calls.
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);
1488 * Used for substituteMethod to indicate default behavior instead of a
1491 SUBSTITUTE_SKIP: SKIP_SYMBOL,
1494 * substituteMethod(object, propertyName, values):
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
1504 substituteMethod(object, propertyName, values) {
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) {
1512 return method.apply(object, arguments);
1514 return values[calls++];
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.
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`);
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;
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);
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;
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);
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;
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);
1598 calls.push(`call ${objectName}.dateAdd`);
1599 if ('dateAdd' in methodOverrides) {
1600 const value = methodOverrides.dateAdd;
1601 return typeof value === "function" ? value(...args) : value;
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);
1611 // Automatically generate the other methods that don't need any custom code
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;
1639 return iso8601[methodName](...args);
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)}`);
1649 calls.push(`has ${formatPropertyName(key, objectName)}`);
1650 return Reflect.has(target, key);
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
1660 calendarThrowEverything() {
1661 class CalendarThrowEverything extends Temporal.Calendar {
1666 TemporalHelpers.assertUnreachable("toString should not be called");
1669 TemporalHelpers.assertUnreachable("dateFromFields should not be called");
1671 yearMonthFromFields() {
1672 TemporalHelpers.assertUnreachable("yearMonthFromFields should not be called");
1674 monthDayFromFields() {
1675 TemporalHelpers.assertUnreachable("monthDayFromFields should not be called");
1678 TemporalHelpers.assertUnreachable("dateAdd should not be called");
1681 TemporalHelpers.assertUnreachable("dateUntil should not be called");
1684 TemporalHelpers.assertUnreachable("era should not be called");
1687 TemporalHelpers.assertUnreachable("eraYear should not be called");
1690 TemporalHelpers.assertUnreachable("year should not be called");
1693 TemporalHelpers.assertUnreachable("month should not be called");
1696 TemporalHelpers.assertUnreachable("monthCode should not be called");
1699 TemporalHelpers.assertUnreachable("day should not be called");
1702 TemporalHelpers.assertUnreachable("fields should not be called");
1705 TemporalHelpers.assertUnreachable("mergeFields should not be called");
1709 return new CalendarThrowEverything();
1713 * oneShiftTimeZone(shiftInstant, shiftNanoseconds):
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.
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.
1725 * It provides a getPossibleInstantsForCalledWith member which is an array
1726 * with the result of calling toString() on any PlainDateTimes passed to
1727 * getPossibleInstantsFor().
1729 oneShiftTimeZone(shiftInstant, shiftNanoseconds) {
1730 class OneShiftTimeZone extends Temporal.TimeZone {
1731 constructor(shiftInstant, shiftNanoseconds) {
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 = [];
1741 _isBeforeShift(instant) {
1742 return instant.epochNanoseconds < this._epoch1;
1745 getOffsetNanosecondsFor(instant) {
1746 return this._isBeforeShift(instant) ? 0 : this._shiftNanoseconds;
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)];
1757 if (instant.epochNanoseconds < this._epoch2) return [instant];
1758 const shifted = instant.subtract(this._shift);
1759 if (this._isBeforeShift(instant)) return [instant, shifted];
1763 getNextTransition(instant) {
1764 return this._isBeforeShift(instant) ? this._shiftInstant : null;
1767 getPreviousTransition(instant) {
1768 return this._isBeforeShift(instant) ? null : this._shiftInstant;
1772 return "Custom/One_Shift";
1775 return new OneShiftTimeZone(shiftInstant, shiftNanoseconds);
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
1789 propertyBagObserver(calls, propertyBag, objectName) {
1790 return new Proxy(propertyBag, {
1792 calls.push(`ownKeys ${objectName}`);
1793 return Reflect.ownKeys(target);
1795 getOwnPropertyDescriptor(target, key) {
1796 calls.push(`getOwnPropertyDescriptor ${formatPropertyName(key, objectName)}`);
1797 return Reflect.getOwnPropertyDescriptor(target, key);
1799 get(target, key, receiver) {
1800 calls.push(`get ${formatPropertyName(key, objectName)}`);
1801 const result = Reflect.get(target, key, receiver);
1802 if (result === undefined) {
1805 if ((result !== null && typeof result === "object") || typeof result === "function") {
1808 return TemporalHelpers.toPrimitiveObserver(calls, result, `${formatPropertyName(key, objectName)}`);
1811 calls.push(`has ${formatPropertyName(key, objectName)}`);
1812 return Reflect.has(target, key);
1818 * specificOffsetTimeZone():
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.
1824 * It also returns an empty array from getPossibleInstantsFor(), so as to
1825 * trigger calls to getOffsetNanosecondsFor() when used from the
1826 * BuiltinTimeZoneGetInstantFor operation.
1828 specificOffsetTimeZone(offsetValue) {
1829 class SpecificOffsetTimeZone extends Temporal.TimeZone {
1830 constructor(offsetValue) {
1832 this._offsetValue = offsetValue;
1835 getOffsetNanosecondsFor() {
1836 return this._offsetValue;
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()];
1846 return this.getOffsetStringFor(new Temporal.Instant(0n));
1849 return new SpecificOffsetTimeZone(offsetValue);
1853 * springForwardFallBackTimeZone():
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.
1859 * The spring-forward occurs at epoch second 954669600 (2000-04-02T02:00
1860 * local) and goes from offset -08:00 to -07:00.
1862 * The fall-back occurs at epoch second 972810000 (2000-10-29T02:00 local) and
1863 * goes from offset -07:00 to -08:00.
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 {
1879 getOffsetNanosecondsFor(instant) {
1880 if (instant.epochNanoseconds < springForwardEpoch ||
1881 instant.epochNanoseconds >= fallBackEpoch) {
1882 return winterOffset.getOffsetNanosecondsFor(instant);
1884 return summerOffset.getOffsetNanosecondsFor(instant);
1887 getPossibleInstantsFor(datetime) {
1888 if (compare(datetime, springForwardLocal) >= 0 && compare(datetime, springForwardLocal.add({ hours: 1 })) < 0) {
1891 if (compare(datetime, fallBackLocal) >= 0 && compare(datetime, fallBackLocal.add({ hours: 1 })) < 0) {
1892 return [summerOffset.getInstantFor(datetime), winterOffset.getInstantFor(datetime)];
1894 if (compare(datetime, springForwardLocal) < 0 || compare(datetime, fallBackLocal) >= 0) {
1895 return [winterOffset.getInstantFor(datetime)];
1897 return [summerOffset.getInstantFor(datetime)];
1900 getPreviousTransition(instant) {
1901 if (instant.epochNanoseconds > fallBackEpoch) return new Temporal.Instant(fallBackEpoch);
1902 if (instant.epochNanoseconds > springForwardEpoch) return new Temporal.Instant(springForwardEpoch);
1906 getNextTransition(instant) {
1907 if (instant.epochNanoseconds < springForwardEpoch) return new Temporal.Instant(springForwardEpoch);
1908 if (instant.epochNanoseconds < fallBackEpoch) return new Temporal.Instant(fallBackEpoch);
1913 return "Custom/Spring_Fall";
1917 return "Custom/Spring_Fall";
1920 return new SpringForwardFallBackTimeZone();
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.
1934 timeZoneObserver(calls, objectName, methodOverrides = {}) {
1935 const utc = new Temporal.TimeZone("UTC");
1936 const trackingMethods = {
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;
1947 return utc[methodName](...args);
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)}`);
1957 calls.push(`has ${formatPropertyName(key, objectName)}`);
1958 return Reflect.has(target, key);
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
1968 timeZoneThrowEverything() {
1969 class TimeZoneThrowEverything extends Temporal.TimeZone {
1973 getOffsetNanosecondsFor() {
1974 TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be called");
1976 getPossibleInstantsFor() {
1977 TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be called");
1980 TemporalHelpers.assertUnreachable("toString should not be called");
1984 return new TimeZoneThrowEverything();
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.
1992 toPrimitiveObserver(calls, primitiveValue, propertyName) {
1995 calls.push(`get ${propertyName}.valueOf`);
1996 return function () {
1997 calls.push(`call ${propertyName}.valueOf`);
1998 return primitiveValue;
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();
2013 * An object containing further methods that return arrays of ISO strings, for
2018 * PlainMonthDay strings that are not valid.
2020 plainMonthDayStringsInvalid() {
2023 "11-18[u-ca=gregory]",
2024 "11-18[u-ca=hebrew]",
2025 "11-18[U-CA=iso8601]",
2026 "11-18[u-CA=iso8601]",
2032 * PlainMonthDay strings that are valid and that should produce October 1st.
2034 plainMonthDayStringsValid() {
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",
2063 * PlainTime strings that may be mistaken for PlainMonthDay or
2064 * PlainYearMonth strings, and so require a time designator.
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
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);
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.
2089 plainTimeStringsUnambiguous() {
2091 "2021-13", // 13 is not a month
2093 "2021-13[-13:00]", // ditto
2094 "202113[-13:00]", // ditto
2095 "0000-00", // 0 is not a month
2097 "0000-00[UTC]", // ditto
2098 "000000[UTC]", // ditto
2099 "1314", // 13 is not a month
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
2110 * PlainYearMonth-like strings that are not valid.
2112 plainYearMonthStringsInvalid() {
2115 "1976-11[u-ca=gregory]",
2116 "1976-11[u-ca=hebrew]",
2117 "1976-11[U-CA=iso8601]",
2118 "1976-11[u-CA=iso8601]",
2124 * PlainYearMonth-like strings that are valid and should produce November
2125 * 1976 in the ISO 8601 calendar.
2127 plainYearMonthStringsValid() {
2131 "1976-11-01T09:00:00+00:00",
2132 "1976-11-01T00:00:00+05:00",
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",
2157 * PlainYearMonth-like strings that are valid and should produce November of
2158 * the ISO year -9999.
2160 plainYearMonthStringsValidNegativeYear() {