diff options
Diffstat (limited to '')
95 files changed, 14536 insertions, 0 deletions
diff --git a/js/src/tests/test262/staging/Temporal/Duration/browser.js b/js/src/tests/test262/staging/Temporal/Duration/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Duration/browser.js diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/add.js b/js/src/tests/test262/staging/Temporal/Duration/old/add.js new file mode 100644 index 0000000000..6e2220b32d --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Duration/old/add.js @@ -0,0 +1,140 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-duration-objects +description: Temporal.Duration.prototype.add() works as expected +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var oneDay = new Temporal.Duration(0, 0, 0, 1); +var hours24 = new Temporal.Duration(0, 0, 0, 0, 24); + +// relativeTo does not affect days if PlainDate +var relativeTo = Temporal.PlainDate.from("2017-01-01"); +assert.sameValue(`${ oneDay.add(hours24, { relativeTo }) }`, "P2D"); + +// relativeTo does not affect days if ZonedDateTime, and duration encompasses no DST change +var relativeTo = Temporal.ZonedDateTime.from("2017-01-01T00:00[+04:30]"); +assert.sameValue(`${ oneDay.add(hours24, { relativeTo }) }`, "P2D"); + +// relativeTo affects days if ZonedDateTime, and duration encompasses DST change +var timeZone = TemporalHelpers.springForwardFallBackTimeZone(); +var skippedHourDay = Temporal.PlainDateTime.from("2000-04-02").toZonedDateTime(timeZone); +var repeatedHourDay = Temporal.PlainDateTime.from("2000-10-29").toZonedDateTime(timeZone); +var inRepeatedHour = new Temporal.ZonedDateTime(972806400_000_000_000n, timeZone); +var hours12 = new Temporal.Duration(0, 0, 0, 0, 12); +var hours25 = new Temporal.Duration(0, 0, 0, 0, 25); + +// start inside repeated hour, end after +assert.sameValue(`${ hours25.add(oneDay, { relativeTo: inRepeatedHour }) }`, "P2D"); +assert.sameValue(`${ oneDay.add(hours25, { relativeTo: inRepeatedHour }) }`, "P2DT1H"); + +// start after repeated hour, end inside (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-10-31T01:00").toZonedDateTime(timeZone); +assert.sameValue(`${ hours25.negated().add(oneDay.negated(), { relativeTo }) }`, "-P2DT1H"); +assert.sameValue(`${ oneDay.negated().add(hours25.negated(), { relativeTo }) }`, "-P2D"); + +// start inside repeated hour, end in skipped hour +assert.sameValue(`${ hours25.add(Temporal.Duration.from({ + days: 125, + hours: 1 +}), { relativeTo: inRepeatedHour }) }`, "P126DT1H"); +assert.sameValue(`${ oneDay.add(Temporal.Duration.from({ + days: 125, + hours: 1 +}), { relativeTo: inRepeatedHour }) }`, "P126DT1H"); + +// start in normal hour, end in skipped hour +var relativeTo = Temporal.PlainDateTime.from("2000-03-31T02:30").toZonedDateTime(timeZone); +assert.sameValue(`${ oneDay.add(hours25, { relativeTo }) }`, "P2DT1H"); +assert.sameValue(`${ hours25.add(oneDay, { relativeTo }) }`, "P2D"); + +// start before skipped hour, end >1 day after +assert.sameValue(`${ hours25.add(oneDay, { relativeTo: skippedHourDay }) }`, "P2DT2H"); +assert.sameValue(`${ oneDay.add(hours25, { relativeTo: skippedHourDay }) }`, "P2DT1H"); + +// start after skipped hour, end >1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-04-03T00:00").toZonedDateTime(timeZone); +assert.sameValue(`${ hours25.negated().add(oneDay.negated(), { relativeTo }) }`, "-P2DT2H"); +assert.sameValue(`${ oneDay.negated().add(hours25.negated(), { relativeTo }) }`, "-P2DT1H"); + +// start before skipped hour, end <1 day after +assert.sameValue(`${ hours12.add(oneDay, { relativeTo: skippedHourDay }) }`, "P1DT13H"); +assert.sameValue(`${ oneDay.add(hours12, { relativeTo: skippedHourDay }) }`, "P1DT12H"); + +// start after skipped hour, end <1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-04-02T12:00").toZonedDateTime(timeZone); +assert.sameValue(`${ hours12.negated().add(oneDay.negated(), { relativeTo }) }`, "-P1DT13H"); +assert.sameValue(`${ oneDay.negated().add(hours12.negated(), { relativeTo }) }`, "-P1DT12H"); + +// start before repeated hour, end >1 day after +assert.sameValue(`${ hours25.add(oneDay, { relativeTo: repeatedHourDay }) }`, "P2D"); +assert.sameValue(`${ oneDay.add(hours25, { relativeTo: repeatedHourDay }) }`, "P2DT1H"); + +// start after repeated hour, end >1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-10-30T00:00").toZonedDateTime(timeZone); +assert.sameValue(`${ hours25.negated().add(oneDay.negated(), { relativeTo }) }`, "-P2D"); +assert.sameValue(`${ oneDay.negated().add(hours25.negated(), { relativeTo }) }`, "-P2DT1H"); + +// start before repeated hour, end <1 day after +assert.sameValue(`${ hours12.add(oneDay, { relativeTo: repeatedHourDay }) }`, "P1DT11H"); +assert.sameValue(`${ oneDay.add(hours12, { relativeTo: repeatedHourDay }) }`, "P1DT12H"); + +// start after repeated hour, end <1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-10-29T12:00").toZonedDateTime(timeZone); +assert.sameValue(`${ hours12.negated().add(oneDay.negated(), { relativeTo }) }`, "-P1DT11H"); +assert.sameValue(`${ oneDay.negated().add(hours12.negated(), { relativeTo }) }`, "-P1DT12H"); + +// Samoa skipped 24 hours +var fakeSamoa = TemporalHelpers.crossDateLineTimeZone(); +var relativeTo = Temporal.PlainDateTime.from("2011-12-29T12:00").toZonedDateTime(fakeSamoa); +assert.sameValue(`${ hours25.add(oneDay, { relativeTo }) }`, "P3DT1H"); +assert.sameValue(`${ oneDay.add(hours25, { relativeTo }) }`, "P3DT1H"); + +// casts relativeTo to ZonedDateTime if possible +assert.sameValue(`${ oneDay.add(hours24, { + relativeTo: { + year: 2000, + month: 10, + day: 28, + timeZone + } +}) }`, "P1DT24H"); + +// casts relativeTo to PlainDate if possible +assert.sameValue(`${ oneDay.add(hours24, { relativeTo: "2019-11-02" }) }`, "P2D"); +assert.sameValue(`${ oneDay.add(hours24, { + relativeTo: { + year: 2019, + month: 11, + day: 2 + } +}) }`, "P2D"); + +// throws on wrong offset for ZonedDateTime relativeTo string +assert.throws(RangeError, () => oneDay.add(hours24, { relativeTo: "1971-01-01T00:00+02:00[-00:44:30]" })); + +// at least the required properties must be present in relativeTo +assert.throws(TypeError, () => oneDay.add(hours24, { + relativeTo: { + month: 11, + day: 3 + } +})); +assert.throws(TypeError, () => oneDay.add(hours24, { + relativeTo: { + year: 2019, + month: 11 + } +})); +assert.throws(TypeError, () => oneDay.add(hours24, { + relativeTo: { + year: 2019, + day: 3 + } +})); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/browser.js b/js/src/tests/test262/staging/Temporal/Duration/old/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Duration/old/browser.js diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/compare-no-precision-loss.js b/js/src/tests/test262/staging/Temporal/Duration/old/compare-no-precision-loss.js new file mode 100644 index 0000000000..e5137e4452 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Duration/old/compare-no-precision-loss.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-duration-objects +description: Temporal.Duration.compare() does not lose precision when totaling everything down to nanoseconds +features: [Temporal] +---*/ + +assert.notSameValue(Temporal.Duration.compare({ days: 200 }, { + days: 200, + nanoseconds: 1 +}), 0); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/limits.js b/js/src/tests/test262/staging/Temporal/Duration/old/limits.js new file mode 100644 index 0000000000..5c1e275430 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Duration/old/limits.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-duration-objects +description: min/max values +features: [Temporal] +---*/ + +var units = [ + "years", + "months", + "weeks", + "days", + "hours", + "minutes", + "seconds", + "milliseconds", + "microseconds", + "nanoseconds" +]; + +// minimum is zero +assert.sameValue(`${ new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 0) }`, "PT0S"); +units.forEach(unit => assert.sameValue(`${ Temporal.Duration.from({ [unit]: 0 }) }`, "PT0S")); +[ + "P0Y", + "P0M", + "P0W", + "P0D", + "PT0H", + "PT0M", + "PT0S" +].forEach(str => assert.sameValue(`${ Temporal.Duration.from(str) }`, "PT0S")); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/round.js b/js/src/tests/test262/staging/Temporal/Duration/old/round.js new file mode 100644 index 0000000000..ccb04aeffc --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Duration/old/round.js @@ -0,0 +1,868 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-duration-objects +description: Temporal.Duration.prototype.round() works as expected +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var d = new Temporal.Duration(5, 5, 5, 5, 5, 5, 5, 5, 5, 5); +var d2 = new Temporal.Duration(0, 0, 0, 5, 5, 5, 5, 5, 5, 5); +var relativeTo = Temporal.PlainDate.from("2020-01-01"); + +// succeeds with largestUnit: 'auto' +assert.sameValue(`${ Temporal.Duration.from({ hours: 25 }).round({ largestUnit: "auto" }) }`, "PT25H"); +var hours25 = new Temporal.Duration(0, 0, 0, 0, 25); + +// days are 24 hours if relativeTo not given +assert.sameValue(`${ hours25.round({ largestUnit: "days" }) }`, "P1DT1H"); + +// days are 24 hours if relativeTo is PlainDate +var relativeTo = Temporal.PlainDate.from("2017-01-01"); +assert.sameValue(`${ hours25.round({ + largestUnit: "days", + relativeTo +}) }`, "P1DT1H"); + +// days are 24 hours if relativeTo is ZonedDateTime, and duration encompasses no DST change +var relativeTo = Temporal.ZonedDateTime.from("2017-01-01T00:00[+04:30]"); +assert.sameValue(`${ hours25.round({ + largestUnit: "days", + relativeTo +}) }`, "P1DT1H"); + +// relativeTo affects days if ZonedDateTime, and duration encompasses DST change +var timeZone = TemporalHelpers.springForwardFallBackTimeZone(); +var skippedHourDay = Temporal.PlainDateTime.from("2000-04-02").toZonedDateTime(timeZone); +var repeatedHourDay = Temporal.PlainDateTime.from("2000-10-29").toZonedDateTime(timeZone); +var inRepeatedHour = new Temporal.ZonedDateTime(972806400_000_000_000n, timeZone); +var oneDay = new Temporal.Duration(0, 0, 0, 1); +var hours12 = new Temporal.Duration(0, 0, 0, 0, 12); + +// start inside repeated hour, end after +assert.sameValue(`${ hours25.round({ + largestUnit: "days", + relativeTo: inRepeatedHour +}) }`, "P1D"); +assert.sameValue(`${ oneDay.round({ + largestUnit: "hours", + relativeTo: inRepeatedHour +}) }`, "PT25H"); + +// start after repeated hour, end inside (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-10-30T01:00").toZonedDateTime(timeZone); +assert.sameValue(`${ hours25.negated().round({ + largestUnit: "days", + relativeTo +}) }`, "-P1D"); +assert.sameValue(`${ oneDay.negated().round({ + largestUnit: "hours", + relativeTo +}) }`, "-PT25H"); + +// start inside repeated hour, end in skipped hour +assert.sameValue(`${ Temporal.Duration.from({ + days: 126, + hours: 1 +}).round({ + largestUnit: "days", + relativeTo: inRepeatedHour +}) }`, "P126DT1H"); +assert.sameValue(`${ Temporal.Duration.from({ + days: 126, + hours: 1 +}).round({ + largestUnit: "hours", + relativeTo: inRepeatedHour +}) }`, "PT3026H"); + +// start in normal hour, end in skipped hour +var relativeTo = Temporal.PlainDateTime.from("2000-04-01T02:30").toZonedDateTime(timeZone); +assert.sameValue(`${ hours25.round({ + largestUnit: "days", + relativeTo +}) }`, "P1DT1H"); +assert.sameValue(`${ oneDay.round({ + largestUnit: "hours", + relativeTo +}) }`, "PT24H"); + +// start before skipped hour, end >1 day after +assert.sameValue(`${ hours25.round({ + largestUnit: "days", + relativeTo: skippedHourDay +}) }`, "P1DT2H"); +assert.sameValue(`${ oneDay.round({ + largestUnit: "hours", + relativeTo: skippedHourDay +}) }`, "PT23H"); + +// start after skipped hour, end >1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-04-03T00:00").toZonedDateTime(timeZone); +assert.sameValue(`${ hours25.negated().round({ + largestUnit: "days", + relativeTo +}) }`, "-P1DT2H"); +assert.sameValue(`${ oneDay.negated().round({ + largestUnit: "hours", + relativeTo +}) }`, "-PT23H"); + +// start before skipped hour, end <1 day after +assert.sameValue(`${ hours12.round({ + largestUnit: "days", + relativeTo: skippedHourDay +}) }`, "PT12H"); + +// start after skipped hour, end <1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-04-02T12:00").toZonedDateTime(timeZone); +assert.sameValue(`${ hours12.negated().round({ + largestUnit: "days", + relativeTo +}) }`, "-PT12H"); + +// start before repeated hour, end >1 day after +assert.sameValue(`${ hours25.round({ + largestUnit: "days", + relativeTo: repeatedHourDay +}) }`, "P1D"); +assert.sameValue(`${ oneDay.round({ + largestUnit: "hours", + relativeTo: repeatedHourDay +}) }`, "PT25H"); + +// start after repeated hour, end >1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-10-30T00:00").toZonedDateTime(timeZone); +assert.sameValue(`${ hours25.negated().round({ + largestUnit: "days", + relativeTo +}) }`, "-P1D"); +assert.sameValue(`${ oneDay.negated().round({ + largestUnit: "hours", + relativeTo +}) }`, "-PT25H"); + +// start before repeated hour, end <1 day after +assert.sameValue(`${ hours12.round({ + largestUnit: "days", + relativeTo: repeatedHourDay +}) }`, "PT12H"); + +// start after repeated hour, end <1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-10-29T12:00").toZonedDateTime(timeZone); +assert.sameValue(`${ hours12.negated().round({ + largestUnit: "days", + relativeTo +}) }`, "-PT12H"); + +// Samoa skipped 24 hours +var fakeSamoa = TemporalHelpers.crossDateLineTimeZone(); +var relativeTo = Temporal.PlainDateTime.from("2011-12-29T12:00").toZonedDateTime(fakeSamoa); +assert.sameValue(`${ hours25.round({ + largestUnit: "days", + relativeTo +}) }`, "P2DT1H"); +assert.sameValue(`${ Temporal.Duration.from({ hours: 48 }).round({ + largestUnit: "days", + relativeTo +}) }`, "P3D"); + +// casts relativeTo to ZonedDateTime if possible +assert.sameValue(`${ hours25.round({ + largestUnit: "days", + relativeTo: { + year: 2000, + month: 10, + day: 29, + timeZone + } +}) }`, "P1D"); + +// casts relativeTo to PlainDate if possible +assert.sameValue(`${ hours25.round({ + largestUnit: "days", + relativeTo: "2019-11-02" +}) }`, "P1DT1H"); +assert.sameValue(`${ hours25.round({ + largestUnit: "days", + relativeTo: { + year: 2019, + month: 11, + day: 2 + } +}) }`, "P1DT1H"); + +// accepts datetime strings or fields for relativeTo +[ + "2020-01-01", + "20200101", + "2020-01-01T00:00:00.000000000", + { + year: 2020, + month: 1, + day: 1 + } +].forEach(relativeTo => { + assert.sameValue(`${ d.round({ + smallestUnit: "seconds", + relativeTo + }) }`, "P5Y6M10DT5H5M5S"); +}); + +// does not accept non-string primitives for relativeTo +[ + 20200101, + 20200101n, + null, + true, +].forEach(relativeTo => { + assert.throws( + TypeError, () => d.round({ smallestUnit: "seconds", relativeTo}) + ); +}); + +// throws on wrong offset for ZonedDateTime relativeTo string +assert.throws(RangeError, () => d.round({ + smallestUnit: "seconds", + relativeTo: "1971-01-01T00:00+02:00[-00:44:30]" +})); + +// relativeTo object must contain at least the required correctly-spelled properties +assert.throws(TypeError, () => hours25.round({ + largestUnit: "days", + relativeTo: { + month: 11, + day: 3 + } +})); +assert.throws(TypeError, () => hours25.round({ + largestUnit: "days", + relativeTo: { + year: 2019, + month: 11 + } +})); +assert.throws(TypeError, () => hours25.round({ + largestUnit: "days", + relativeTo: { + year: 2019, + day: 3 + } +})); + +// incorrectly-spelled properties are ignored in relativeTo +var oneMonth = Temporal.Duration.from({ months: 1 }); +assert.sameValue(`${ oneMonth.round({ + largestUnit: "days", + relativeTo: { + year: 2020, + month: 1, + day: 1, + months: 2 + } +}) }`, "P31D"); + +// throws if neither one of largestUnit or smallestUnit is given +var hoursOnly = new Temporal.Duration(0, 0, 0, 0, 1); +[ + {}, + () => { + }, + { roundingMode: "ceil" } +].forEach(roundTo => { + assert.throws(RangeError, () => d.round(roundTo)); + assert.throws(RangeError, () => hoursOnly.round(roundTo)); +}); + +// relativeTo not required to round non-calendar units in durations w/o calendar units (string param) +assert.sameValue(`${ d2.round("days") }`, "P5D"); +assert.sameValue(`${ d2.round("hours") }`, "P5DT5H"); +assert.sameValue(`${ d2.round("minutes") }`, "P5DT5H5M"); +assert.sameValue(`${ d2.round("seconds") }`, "P5DT5H5M5S"); +assert.sameValue(`${ d2.round("milliseconds") }`, "P5DT5H5M5.005S"); +assert.sameValue(`${ d2.round("microseconds") }`, "P5DT5H5M5.005005S"); +assert.sameValue(`${ d2.round("nanoseconds") }`, "P5DT5H5M5.005005005S"); + +// relativeTo is required to round calendar units even in durations w/o calendar units (string param) +assert.throws(RangeError, () => d2.round("years")); +assert.throws(RangeError, () => d2.round("months")); +assert.throws(RangeError, () => d2.round("weeks")); + +// relativeTo not required to round non-calendar units in durations w/o calendar units (object param) +assert.sameValue(`${ d2.round({ smallestUnit: "days" }) }`, "P5D"); +assert.sameValue(`${ d2.round({ smallestUnit: "hours" }) }`, "P5DT5H"); +assert.sameValue(`${ d2.round({ smallestUnit: "minutes" }) }`, "P5DT5H5M"); +assert.sameValue(`${ d2.round({ smallestUnit: "seconds" }) }`, "P5DT5H5M5S"); +assert.sameValue(`${ d2.round({ smallestUnit: "milliseconds" }) }`, "P5DT5H5M5.005S"); +assert.sameValue(`${ d2.round({ smallestUnit: "microseconds" }) }`, "P5DT5H5M5.005005S"); +assert.sameValue(`${ d2.round({ smallestUnit: "nanoseconds" }) }`, "P5DT5H5M5.005005005S"); + +// relativeTo is required to round calendar units even in durations w/o calendar units (object param) +assert.throws(RangeError, () => d2.round({ smallestUnit: "years" })); +assert.throws(RangeError, () => d2.round({ smallestUnit: "months" })); +assert.throws(RangeError, () => d2.round({ smallestUnit: "weeks" })); + +// relativeTo is required for rounding durations with calendar units +assert.throws(RangeError, () => d.round({ largestUnit: "years" })); +assert.throws(RangeError, () => d.round({ largestUnit: "months" })); +assert.throws(RangeError, () => d.round({ largestUnit: "weeks" })); +assert.throws(RangeError, () => d.round({ largestUnit: "days" })); +assert.throws(RangeError, () => d.round({ largestUnit: "hours" })); +assert.throws(RangeError, () => d.round({ largestUnit: "minutes" })); +assert.throws(RangeError, () => d.round({ largestUnit: "seconds" })); +assert.throws(RangeError, () => d.round({ largestUnit: "milliseconds" })); +assert.throws(RangeError, () => d.round({ largestUnit: "microseconds" })); +assert.throws(RangeError, () => d.round({ largestUnit: "nanoseconds" })); + +// durations do not balance beyond their current largest unit by default +var relativeTo = Temporal.PlainDate.from("2020-01-01"); +var fortyDays = Temporal.Duration.from({ days: 40 }); +assert.sameValue(`${ fortyDays.round({ smallestUnit: "seconds" }) }`, "P40D"); +var roundAndBalanceResults = { + years: { + years: "P6Y", + months: "P5Y6M", + weeks: "P5Y5M6W", + days: "P5Y6M10D", + hours: "P5Y6M10DT5H", + minutes: "P5Y6M10DT5H5M", + seconds: "P5Y6M10DT5H5M5S", + milliseconds: "P5Y6M10DT5H5M5.005S", + microseconds: "P5Y6M10DT5H5M5.005005S", + nanoseconds: "P5Y6M10DT5H5M5.005005005S" + }, + months: { + months: "P66M", + weeks: "P65M6W", + days: "P66M10D", + hours: "P66M10DT5H", + minutes: "P66M10DT5H5M", + seconds: "P66M10DT5H5M5S", + milliseconds: "P66M10DT5H5M5.005S", + microseconds: "P66M10DT5H5M5.005005S", + nanoseconds: "P66M10DT5H5M5.005005005S" + }, + weeks: { + weeks: "P288W", + days: "P288W2D", + hours: "P288W2DT5H", + minutes: "P288W2DT5H5M", + seconds: "P288W2DT5H5M5S", + milliseconds: "P288W2DT5H5M5.005S", + microseconds: "P288W2DT5H5M5.005005S", + nanoseconds: "P288W2DT5H5M5.005005005S" + }, + days: { + days: "P2018D", + hours: "P2018DT5H", + minutes: "P2018DT5H5M", + seconds: "P2018DT5H5M5S", + milliseconds: "P2018DT5H5M5.005S", + microseconds: "P2018DT5H5M5.005005S", + nanoseconds: "P2018DT5H5M5.005005005S" + }, + hours: { + hours: "PT48437H", + minutes: "PT48437H5M", + seconds: "PT48437H5M5S", + milliseconds: "PT48437H5M5.005S", + microseconds: "PT48437H5M5.005005S", + nanoseconds: "PT48437H5M5.005005005S" + }, + minutes: { + minutes: "PT2906225M", + seconds: "PT2906225M5S", + milliseconds: "PT2906225M5.005S", + microseconds: "PT2906225M5.005005S", + nanoseconds: "PT2906225M5.005005005S" + }, + seconds: { + seconds: "PT174373505S", + milliseconds: "PT174373505.005S", + microseconds: "PT174373505.005005S", + nanoseconds: "PT174373505.005005005S" + }, + milliseconds: { + milliseconds: "PT174373505.005S", + microseconds: "PT174373505.005005S", + nanoseconds: "PT174373505.005005005S" + } +}; +for (var [largestUnit, entry] of Object.entries(roundAndBalanceResults)) { + for (var [smallestUnit, expected] of Object.entries(entry)) { + assert.sameValue(`${ d.round({ + largestUnit, + smallestUnit, + relativeTo + }) }`, expected); + } +} +var balanceLosePrecisionResults = { + microseconds: [ + "microseconds", + "nanoseconds" + ], + nanoseconds: ["nanoseconds"] +}; + +// Round may lose precision below ms +for (var [largestUnit, entry] of Object.entries(balanceLosePrecisionResults)) { + for (var smallestUnit of entry) { + assert(`${ d.round({ + largestUnit, + smallestUnit, + relativeTo + }) }`.startsWith("PT174373505.005")); + } +} + +// halfExpand is the default +assert.sameValue(`${ d.round({ + smallestUnit: "years", + relativeTo +}) }`, "P6Y"); +assert.sameValue(`${ d.negated().round({ + smallestUnit: "years", + relativeTo +}) }`, "-P6Y"); + +// balances up differently depending on relativeTo +var fortyDays = Temporal.Duration.from({ days: 40 }); +assert.sameValue(`${ fortyDays.round({ + largestUnit: "years", + relativeTo: "2020-01-01" +}) }`, "P1M9D"); +assert.sameValue(`${ fortyDays.round({ + largestUnit: "years", + relativeTo: "2020-02-01" +}) }`, "P1M11D"); +assert.sameValue(`${ fortyDays.round({ + largestUnit: "years", + relativeTo: "2020-03-01" +}) }`, "P1M9D"); +assert.sameValue(`${ fortyDays.round({ + largestUnit: "years", + relativeTo: "2020-04-01" +}) }`, "P1M10D"); +var minusForty = Temporal.Duration.from({ days: -40 }); +assert.sameValue(`${ minusForty.round({ + largestUnit: "years", + relativeTo: "2020-02-01" +}) }`, "-P1M9D"); +assert.sameValue(`${ minusForty.round({ + largestUnit: "years", + relativeTo: "2020-01-01" +}) }`, "-P1M9D"); +assert.sameValue(`${ minusForty.round({ + largestUnit: "years", + relativeTo: "2020-03-01" +}) }`, "-P1M11D"); +assert.sameValue(`${ minusForty.round({ + largestUnit: "years", + relativeTo: "2020-04-01" +}) }`, "-P1M9D"); + +// balances up to the next unit after rounding +var almostWeek = Temporal.Duration.from({ + days: 6, + hours: 20 +}); +assert.sameValue(`${ almostWeek.round({ + largestUnit: "weeks", + smallestUnit: "days", + relativeTo: "2020-01-01" +}) }`, "P1W"); + +// balances days up to both years and months +var twoYears = Temporal.Duration.from({ + months: 11, + days: 396 +}); +assert.sameValue(`${ twoYears.round({ + largestUnit: "years", + relativeTo: "2017-01-01" +}) }`, "P2Y"); + +// does not balance up to weeks if largestUnit is larger than weeks +var monthAlmostWeek = Temporal.Duration.from({ + months: 1, + days: 6, + hours: 20 +}); +assert.sameValue(`${ monthAlmostWeek.round({ + smallestUnit: "days", + relativeTo: "2020-01-01" +}) }`, "P1M7D"); + +// balances down differently depending on relativeTo +var oneYear = Temporal.Duration.from({ years: 1 }); +assert.sameValue(`${ oneYear.round({ + largestUnit: "days", + relativeTo: "2019-01-01" +}) }`, "P365D"); +assert.sameValue(`${ oneYear.round({ + largestUnit: "days", + relativeTo: "2019-07-01" +}) }`, "P366D"); +var minusYear = Temporal.Duration.from({ years: -1 }); +assert.sameValue(`${ minusYear.round({ + largestUnit: "days", + relativeTo: "2020-01-01" +}) }`, "-P365D"); +assert.sameValue(`${ minusYear.round({ + largestUnit: "days", + relativeTo: "2020-07-01" +}) }`, "-P366D"); + +// rounds to an increment of hours +assert.sameValue(`${ d.round({ + smallestUnit: "hours", + roundingIncrement: 3, + relativeTo +}) }`, "P5Y6M10DT6H"); + +// rounds to an increment of minutes +assert.sameValue(`${ d.round({ + smallestUnit: "minutes", + roundingIncrement: 30, + relativeTo +}) }`, "P5Y6M10DT5H"); + +// rounds to an increment of seconds +assert.sameValue(`${ d.round({ + smallestUnit: "seconds", + roundingIncrement: 15, + relativeTo +}) }`, "P5Y6M10DT5H5M"); + +// rounds to an increment of milliseconds +assert.sameValue(`${ d.round({ + smallestUnit: "milliseconds", + roundingIncrement: 10, + relativeTo +}) }`, "P5Y6M10DT5H5M5.01S"); + +// rounds to an increment of microseconds +assert.sameValue(`${ d.round({ + smallestUnit: "microseconds", + roundingIncrement: 10, + relativeTo +}) }`, "P5Y6M10DT5H5M5.00501S"); + +// rounds to an increment of nanoseconds +assert.sameValue(`${ d.round({ + smallestUnit: "nanoseconds", + roundingIncrement: 10, + relativeTo +}) }`, "P5Y6M10DT5H5M5.00500501S"); + +// valid hour increments divide into 24 +[ + 1, + 2, + 3, + 4, + 6, + 8, + 12 +].forEach(roundingIncrement => { + var options = { + smallestUnit: "hours", + roundingIncrement, + relativeTo + }; + assert(d.round(options) instanceof Temporal.Duration); +}); +[ + "minutes", + "seconds" +].forEach(smallestUnit => { + // valid minutes/seconds increments divide into 60 + [ + 1, + 2, + 3, + 4, + 5, + 6, + 10, + 12, + 15, + 20, + 30 + ].forEach(roundingIncrement => { + var roundTo = { + smallestUnit, + roundingIncrement, + relativeTo + }; + assert(d.round(roundTo) instanceof Temporal.Duration); + }); + }); +[ + "milliseconds", + "microseconds", + "nanoseconds" +].forEach(smallestUnit => { + // valid milliseconds/microseconds/nanoseconds increments divide into 1000 + [ + 1, + 2, + 4, + 5, + 8, + 10, + 20, + 25, + 40, + 50, + 100, + 125, + 200, + 250, + 500 + ].forEach(roundingIncrement => { + var roundTo = { + smallestUnit, + roundingIncrement, + relativeTo + }; + assert(d.round(roundTo) instanceof Temporal.Duration); + }); + }); + +// throws on increments that do not divide evenly into the next highest +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "hours", + roundingIncrement: 11 +})); +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "minutes", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "seconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "milliseconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "microseconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "nanoseconds", + roundingIncrement: 29 +})); + +// throws on increments that are equal to the next highest +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "hours", + roundingIncrement: 24 +})); +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "minutes", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "seconds", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "milliseconds", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "microseconds", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "nanoseconds", + roundingIncrement: 1000 +})); + +// accepts singular units +assert.sameValue(`${ d.round({ + largestUnit: "year", + relativeTo +}) }`, `${ d.round({ + largestUnit: "years", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + smallestUnit: "year", + relativeTo +}) }`, `${ d.round({ + smallestUnit: "years", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + largestUnit: "month", + relativeTo +}) }`, `${ d.round({ + largestUnit: "months", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + smallestUnit: "month", + relativeTo +}) }`, `${ d.round({ + smallestUnit: "months", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + largestUnit: "day", + relativeTo +}) }`, `${ d.round({ + largestUnit: "days", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + smallestUnit: "day", + relativeTo +}) }`, `${ d.round({ + smallestUnit: "days", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + largestUnit: "hour", + relativeTo +}) }`, `${ d.round({ + largestUnit: "hours", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + smallestUnit: "hour", + relativeTo +}) }`, `${ d.round({ + smallestUnit: "hours", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + largestUnit: "minute", + relativeTo +}) }`, `${ d.round({ + largestUnit: "minutes", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + smallestUnit: "minute", + relativeTo +}) }`, `${ d.round({ + smallestUnit: "minutes", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + largestUnit: "second", + relativeTo +}) }`, `${ d.round({ + largestUnit: "seconds", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + smallestUnit: "second", + relativeTo +}) }`, `${ d.round({ + smallestUnit: "seconds", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + largestUnit: "millisecond", + relativeTo +}) }`, `${ d.round({ + largestUnit: "milliseconds", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + smallestUnit: "millisecond", + relativeTo +}) }`, `${ d.round({ + smallestUnit: "milliseconds", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + largestUnit: "microsecond", + relativeTo +}) }`, `${ d.round({ + largestUnit: "microseconds", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + smallestUnit: "microsecond", + relativeTo +}) }`, `${ d.round({ + smallestUnit: "microseconds", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + largestUnit: "nanosecond", + relativeTo +}) }`, `${ d.round({ + largestUnit: "nanoseconds", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + smallestUnit: "nanosecond", + relativeTo +}) }`, `${ d.round({ + smallestUnit: "nanoseconds", + relativeTo +}) }`); + +// counts the correct number of days when rounding relative to a date +var days = Temporal.Duration.from({ days: 45 }); +assert.sameValue(`${ days.round({ + relativeTo: "2019-01-01", + smallestUnit: "months" +}) }`, "P2M"); +assert.sameValue(`${ days.negated().round({ + relativeTo: "2019-02-15", + smallestUnit: "months" +}) }`, "-P1M"); +var yearAndHalf = Temporal.Duration.from({ + days: 547, + hours: 12 +}); +assert.sameValue(`${ yearAndHalf.round({ + relativeTo: "2018-01-01", + smallestUnit: "years" +}) }`, "P2Y"); +assert.sameValue(`${ yearAndHalf.round({ + relativeTo: "2018-07-01", + smallestUnit: "years" +}) }`, "P1Y"); +assert.sameValue(`${ yearAndHalf.round({ + relativeTo: "2019-01-01", + smallestUnit: "years" +}) }`, "P1Y"); +assert.sameValue(`${ yearAndHalf.round({ + relativeTo: "2019-07-01", + smallestUnit: "years" +}) }`, "P1Y"); +assert.sameValue(`${ yearAndHalf.round({ + relativeTo: "2020-01-01", + smallestUnit: "years" +}) }`, "P1Y"); +assert.sameValue(`${ yearAndHalf.round({ + relativeTo: "2020-07-01", + smallestUnit: "years" +}) }`, "P2Y"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/shell.js b/js/src/tests/test262/staging/Temporal/Duration/old/shell.js new file mode 100644 index 0000000000..60f74c2518 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Duration/old/shell.js @@ -0,0 +1,2158 @@ +// GENERATED, DO NOT EDIT +// file: temporalHelpers.js +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: | + This defines helper objects and functions for testing Temporal. +defines: [TemporalHelpers] +features: [Symbol.species, Symbol.iterator, Temporal] +---*/ + +const ASCII_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/u; + +function formatPropertyName(propertyKey, objectName = "") { + switch (typeof propertyKey) { + case "symbol": + if (Symbol.keyFor(propertyKey) !== undefined) { + return `${objectName}[Symbol.for('${Symbol.keyFor(propertyKey)}')]`; + } else if (propertyKey.description.startsWith('Symbol.')) { + return `${objectName}[${propertyKey.description}]`; + } else { + return `${objectName}[Symbol('${propertyKey.description}')]` + } + case "string": + if (propertyKey !== String(Number(propertyKey))) { + if (ASCII_IDENTIFIER.test(propertyKey)) { + return objectName ? `${objectName}.${propertyKey}` : propertyKey; + } + return `${objectName}['${propertyKey.replace(/'/g, "\\'")}']` + } + // fall through + default: + // integer or string integer-index + return `${objectName}[${propertyKey}]`; + } +} + +const SKIP_SYMBOL = Symbol("Skip"); + +var TemporalHelpers = { + /* + * Codes and maximum lengths of months in the ISO 8601 calendar. + */ + ISOMonths: [ + { month: 1, monthCode: "M01", daysInMonth: 31 }, + { month: 2, monthCode: "M02", daysInMonth: 29 }, + { month: 3, monthCode: "M03", daysInMonth: 31 }, + { month: 4, monthCode: "M04", daysInMonth: 30 }, + { month: 5, monthCode: "M05", daysInMonth: 31 }, + { month: 6, monthCode: "M06", daysInMonth: 30 }, + { month: 7, monthCode: "M07", daysInMonth: 31 }, + { month: 8, monthCode: "M08", daysInMonth: 31 }, + { month: 9, monthCode: "M09", daysInMonth: 30 }, + { month: 10, monthCode: "M10", daysInMonth: 31 }, + { month: 11, monthCode: "M11", daysInMonth: 30 }, + { month: 12, monthCode: "M12", daysInMonth: 31 } + ], + + /* + * assertDuration(duration, years, ..., nanoseconds[, description]): + * + * Shorthand for asserting that each field of a Temporal.Duration is equal to + * an expected value. + */ + assertDuration(duration, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(duration instanceof Temporal.Duration, `${prefix}instanceof`); + assert.sameValue(duration.years, years, `${prefix}years result:`); + assert.sameValue(duration.months, months, `${prefix}months result:`); + assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`); + assert.sameValue(duration.days, days, `${prefix}days result:`); + assert.sameValue(duration.hours, hours, `${prefix}hours result:`); + assert.sameValue(duration.minutes, minutes, `${prefix}minutes result:`); + assert.sameValue(duration.seconds, seconds, `${prefix}seconds result:`); + assert.sameValue(duration.milliseconds, milliseconds, `${prefix}milliseconds result:`); + assert.sameValue(duration.microseconds, microseconds, `${prefix}microseconds result:`); + assert.sameValue(duration.nanoseconds, nanoseconds, `${prefix}nanoseconds result`); + }, + + /* + * assertDateDuration(duration, years, months, weeks, days, [, description]): + * + * Shorthand for asserting that each date field of a Temporal.Duration is + * equal to an expected value. + */ + assertDateDuration(duration, years, months, weeks, days, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(duration instanceof Temporal.Duration, `${prefix}instanceof`); + assert.sameValue(duration.years, years, `${prefix}years result:`); + assert.sameValue(duration.months, months, `${prefix}months result:`); + assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`); + assert.sameValue(duration.days, days, `${prefix}days result:`); + assert.sameValue(duration.hours, 0, `${prefix}hours result should be zero:`); + assert.sameValue(duration.minutes, 0, `${prefix}minutes result should be zero:`); + assert.sameValue(duration.seconds, 0, `${prefix}seconds result should be zero:`); + assert.sameValue(duration.milliseconds, 0, `${prefix}milliseconds result should be zero:`); + assert.sameValue(duration.microseconds, 0, `${prefix}microseconds result should be zero:`); + assert.sameValue(duration.nanoseconds, 0, `${prefix}nanoseconds result should be zero:`); + }, + + /* + * assertDurationsEqual(actual, expected[, description]): + * + * Shorthand for asserting that each field of a Temporal.Duration is equal to + * the corresponding field in another Temporal.Duration. + */ + assertDurationsEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.Duration, `${prefix}expected value should be a Temporal.Duration`); + TemporalHelpers.assertDuration(actual, expected.years, expected.months, expected.weeks, expected.days, expected.hours, expected.minutes, expected.seconds, expected.milliseconds, expected.microseconds, expected.nanoseconds, description); + }, + + /* + * assertInstantsEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.Instants are of the correct type + * and equal according to their equals() methods. + */ + assertInstantsEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.Instant, `${prefix}expected value should be a Temporal.Instant`); + assert(actual instanceof Temporal.Instant, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + }, + + /* + * assertPlainDate(date, year, ..., nanosecond[, description[, era, eraYear]]): + * + * Shorthand for asserting that each field of a Temporal.PlainDate is equal to + * an expected value. (Except the `calendar` property, since callers may want + * to assert either object equality with an object they put in there, or the + * value of date.calendarId.) + */ + assertPlainDate(date, year, month, monthCode, day, description = "", era = undefined, eraYear = undefined) { + const prefix = description ? `${description}: ` : ""; + assert(date instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert.sameValue(date.era, era, `${prefix}era result:`); + assert.sameValue(date.eraYear, eraYear, `${prefix}eraYear result:`); + assert.sameValue(date.year, year, `${prefix}year result:`); + assert.sameValue(date.month, month, `${prefix}month result:`); + assert.sameValue(date.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(date.day, day, `${prefix}day result:`); + }, + + /* + * assertPlainDateTime(datetime, year, ..., nanosecond[, description[, era, eraYear]]): + * + * Shorthand for asserting that each field of a Temporal.PlainDateTime is + * equal to an expected value. (Except the `calendar` property, since callers + * may want to assert either object equality with an object they put in there, + * or the value of datetime.calendarId.) + */ + assertPlainDateTime(datetime, year, month, monthCode, day, hour, minute, second, millisecond, microsecond, nanosecond, description = "", era = undefined, eraYear = undefined) { + const prefix = description ? `${description}: ` : ""; + assert(datetime instanceof Temporal.PlainDateTime, `${prefix}instanceof`); + assert.sameValue(datetime.era, era, `${prefix}era result:`); + assert.sameValue(datetime.eraYear, eraYear, `${prefix}eraYear result:`); + assert.sameValue(datetime.year, year, `${prefix}year result:`); + assert.sameValue(datetime.month, month, `${prefix}month result:`); + assert.sameValue(datetime.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(datetime.day, day, `${prefix}day result:`); + assert.sameValue(datetime.hour, hour, `${prefix}hour result:`); + assert.sameValue(datetime.minute, minute, `${prefix}minute result:`); + assert.sameValue(datetime.second, second, `${prefix}second result:`); + assert.sameValue(datetime.millisecond, millisecond, `${prefix}millisecond result:`); + assert.sameValue(datetime.microsecond, microsecond, `${prefix}microsecond result:`); + assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); + }, + + /* + * assertPlainDateTimesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDateTimes are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDateTimesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDateTime, `${prefix}expected value should be a Temporal.PlainDateTime`); + assert(actual instanceof Temporal.PlainDateTime, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.getISOFields().calendar, + expected.getISOFields().calendar, + `${prefix}calendar same value:` + ); + }, + + /* + * assertPlainMonthDay(monthDay, monthCode, day[, description [, referenceISOYear]]): + * + * Shorthand for asserting that each field of a Temporal.PlainMonthDay is + * equal to an expected value. (Except the `calendar` property, since callers + * may want to assert either object equality with an object they put in there, + * or the value of monthDay.calendarId().) + */ + assertPlainMonthDay(monthDay, monthCode, day, description = "", referenceISOYear = 1972) { + const prefix = description ? `${description}: ` : ""; + assert(monthDay instanceof Temporal.PlainMonthDay, `${prefix}instanceof`); + assert.sameValue(monthDay.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(monthDay.day, day, `${prefix}day result:`); + assert.sameValue(monthDay.getISOFields().isoYear, referenceISOYear, `${prefix}referenceISOYear result:`); + }, + + /* + * assertPlainTime(time, hour, ..., nanosecond[, description]): + * + * Shorthand for asserting that each field of a Temporal.PlainTime is equal to + * an expected value. + */ + assertPlainTime(time, hour, minute, second, millisecond, microsecond, nanosecond, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(time instanceof Temporal.PlainTime, `${prefix}instanceof`); + assert.sameValue(time.hour, hour, `${prefix}hour result:`); + assert.sameValue(time.minute, minute, `${prefix}minute result:`); + assert.sameValue(time.second, second, `${prefix}second result:`); + assert.sameValue(time.millisecond, millisecond, `${prefix}millisecond result:`); + assert.sameValue(time.microsecond, microsecond, `${prefix}microsecond result:`); + assert.sameValue(time.nanosecond, nanosecond, `${prefix}nanosecond result:`); + }, + + /* + * assertPlainTimesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainTimes are of the correct + * type and equal according to their equals() methods. + */ + assertPlainTimesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainTime, `${prefix}expected value should be a Temporal.PlainTime`); + assert(actual instanceof Temporal.PlainTime, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + }, + + /* + * assertPlainYearMonth(yearMonth, year, month, monthCode[, description[, era, eraYear, referenceISODay]]): + * + * Shorthand for asserting that each field of a Temporal.PlainYearMonth is + * equal to an expected value. (Except the `calendar` property, since callers + * may want to assert either object equality with an object they put in there, + * or the value of yearMonth.calendarId.) + */ + assertPlainYearMonth(yearMonth, year, month, monthCode, description = "", era = undefined, eraYear = undefined, referenceISODay = 1) { + const prefix = description ? `${description}: ` : ""; + assert(yearMonth instanceof Temporal.PlainYearMonth, `${prefix}instanceof`); + assert.sameValue(yearMonth.era, era, `${prefix}era result:`); + assert.sameValue(yearMonth.eraYear, eraYear, `${prefix}eraYear result:`); + assert.sameValue(yearMonth.year, year, `${prefix}year result:`); + assert.sameValue(yearMonth.month, month, `${prefix}month result:`); + assert.sameValue(yearMonth.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(yearMonth.getISOFields().isoDay, referenceISODay, `${prefix}referenceISODay result:`); + }, + + /* + * assertZonedDateTimesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.ZonedDateTimes are of the correct + * type, equal according to their equals() methods, and additionally that + * their time zones and calendar internal slots are the same value. + */ + assertZonedDateTimesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.ZonedDateTime, `${prefix}expected value should be a Temporal.ZonedDateTime`); + assert(actual instanceof Temporal.ZonedDateTime, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue(actual.timeZone, expected.timeZone, `${prefix}time zone same value:`); + assert.sameValue( + actual.getISOFields().calendar, + expected.getISOFields().calendar, + `${prefix}calendar same value:` + ); + }, + + /* + * assertUnreachable(description): + * + * Helper for asserting that code is not executed. This is useful for + * assertions that methods of user calendars and time zones are not called. + */ + assertUnreachable(description) { + let message = "This code should not be executed"; + if (description) { + message = `${message}: ${description}`; + } + throw new Test262Error(message); + }, + + /* + * checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls): + * + * When an options object with a largestUnit property is synthesized inside + * Temporal and passed to user code such as calendar.dateUntil(), the value of + * the largestUnit property should be in the singular form, even if the input + * was given in the plural form. + * (This doesn't apply when the options object is passed through verbatim.) + * + * func(calendar, largestUnit, index) is the operation under test. It's called + * with an instance of a calendar that keeps track of which largestUnit is + * passed to dateUntil(), each key of expectedLargestUnitCalls in turn, and + * the key's numerical index in case the function needs to generate test data + * based on the index. At the end, the actual values passed to dateUntil() are + * compared with the array values of expectedLargestUnitCalls. + */ + checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls) { + const actual = []; + + class DateUntilOptionsCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateUntil(earlier, later, options) { + actual.push(options.largestUnit); + return super.dateUntil(earlier, later, options); + } + + toString() { + return "date-until-options"; + } + } + + const calendar = new DateUntilOptionsCalendar(); + Object.entries(expectedLargestUnitCalls).forEach(([largestUnit, expected], index) => { + func(calendar, largestUnit, index); + assert.compareArray(actual, expected, `largestUnit passed to calendar.dateUntil() for largestUnit ${largestUnit}`); + actual.splice(0); // empty it for the next check + }); + }, + + /* + * checkPlainDateTimeConversionFastPath(func): + * + * ToTemporalDate and ToTemporalTime should both, if given a + * Temporal.PlainDateTime instance, convert to the desired type by reading the + * PlainDateTime's internal slots, rather than calling any getters. + * + * func(datetime, calendar) is the actual operation to test, that must + * internally call the abstract operation ToTemporalDate or ToTemporalTime. + * It is passed a Temporal.PlainDateTime instance, as well as the instance's + * calendar object (so that it doesn't have to call the calendar getter itself + * if it wants to make any assertions about the calendar.) + */ + checkPlainDateTimeConversionFastPath(func, message = "checkPlainDateTimeConversionFastPath") { + const actual = []; + const expected = []; + + const calendar = new Temporal.Calendar("iso8601"); + const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar); + const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDateTime.prototype); + ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => { + Object.defineProperty(datetime, property, { + get() { + actual.push(`get ${formatPropertyName(property)}`); + const value = prototypeDescrs[property].get.call(this); + return { + toString() { + actual.push(`toString ${formatPropertyName(property)}`); + return value.toString(); + }, + valueOf() { + actual.push(`valueOf ${formatPropertyName(property)}`); + return value; + }, + }; + }, + }); + }); + Object.defineProperty(datetime, "calendar", { + get() { + actual.push("get calendar"); + return calendar; + }, + }); + + func(datetime, calendar); + assert.compareArray(actual, expected, `${message}: property getters not called`); + }, + + /* + * Check that an options bag that accepts units written in the singular form, + * also accepts the same units written in the plural form. + * func(unit) should call the method with the appropriate options bag + * containing unit as a value. This will be called twice for each element of + * validSingularUnits, once with singular and once with plural, and the + * results of each pair should be the same (whether a Temporal object or a + * primitive value.) + */ + checkPluralUnitsAccepted(func, validSingularUnits) { + const plurals = { + year: 'years', + month: 'months', + week: 'weeks', + day: 'days', + hour: 'hours', + minute: 'minutes', + second: 'seconds', + millisecond: 'milliseconds', + microsecond: 'microseconds', + nanosecond: 'nanoseconds', + }; + + validSingularUnits.forEach((unit) => { + const singularValue = func(unit); + const pluralValue = func(plurals[unit]); + const desc = `Plural ${plurals[unit]} produces the same result as singular ${unit}`; + if (singularValue instanceof Temporal.Duration) { + TemporalHelpers.assertDurationsEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.Instant) { + TemporalHelpers.assertInstantsEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.PlainDateTime) { + TemporalHelpers.assertPlainDateTimesEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.PlainTime) { + TemporalHelpers.assertPlainTimesEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.ZonedDateTime) { + TemporalHelpers.assertZonedDateTimesEqual(pluralValue, singularValue, desc); + } else { + assert.sameValue(pluralValue, singularValue); + } + }); + }, + + /* + * checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc): + * + * Checks the type handling of the roundingIncrement option. + * checkFunc(roundingIncrement) is a function which takes the value of + * roundingIncrement to test, and calls the method under test with it, + * returning the result. assertTrueResultFunc(result, description) should + * assert that result is the expected result with roundingIncrement: true, and + * assertObjectResultFunc(result, description) should assert that result is + * the expected result with roundingIncrement being an object with a valueOf() + * method. + */ + checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc) { + // null converts to 0, which is out of range + assert.throws(RangeError, () => checkFunc(null), "null"); + // Booleans convert to either 0 or 1, and 1 is allowed + const trueResult = checkFunc(true); + assertTrueResultFunc(trueResult, "true"); + assert.throws(RangeError, () => checkFunc(false), "false"); + // Symbols and BigInts cannot convert to numbers + assert.throws(TypeError, () => checkFunc(Symbol()), "symbol"); + assert.throws(TypeError, () => checkFunc(2n), "bigint"); + + // Objects prefer their valueOf() methods when converting to a number + assert.throws(RangeError, () => checkFunc({}), "plain object"); + + const expected = [ + "get roundingIncrement.valueOf", + "call roundingIncrement.valueOf", + ]; + const actual = []; + const observer = TemporalHelpers.toPrimitiveObserver(actual, 2, "roundingIncrement"); + const objectResult = checkFunc(observer); + assertObjectResultFunc(objectResult, "object with valueOf"); + assert.compareArray(actual, expected, "order of operations"); + }, + + /* + * checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc): + * + * Checks the type handling of a string option, of which there are several in + * Temporal. + * propertyName is the name of the option, and value is the value that + * assertFunc should expect it to have. + * checkFunc(value) is a function which takes the value of the option to test, + * and calls the method under test with it, returning the result. + * assertFunc(result, description) should assert that result is the expected + * result with the option value being an object with a toString() method + * which returns the given value. + */ + checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc) { + // null converts to the string "null", which is an invalid string value + assert.throws(RangeError, () => checkFunc(null), "null"); + // Booleans convert to the strings "true" or "false", which are invalid + assert.throws(RangeError, () => checkFunc(true), "true"); + assert.throws(RangeError, () => checkFunc(false), "false"); + // Symbols cannot convert to strings + assert.throws(TypeError, () => checkFunc(Symbol()), "symbol"); + // Numbers convert to strings which are invalid + assert.throws(RangeError, () => checkFunc(2), "number"); + // BigInts convert to strings which are invalid + assert.throws(RangeError, () => checkFunc(2n), "bigint"); + + // Objects prefer their toString() methods when converting to a string + assert.throws(RangeError, () => checkFunc({}), "plain object"); + + const expected = [ + `get ${propertyName}.toString`, + `call ${propertyName}.toString`, + ]; + const actual = []; + const observer = TemporalHelpers.toPrimitiveObserver(actual, value, propertyName); + const result = checkFunc(observer); + assertFunc(result, "object with toString"); + assert.compareArray(actual, expected, "order of operations"); + }, + + /* + * checkSubclassingIgnored(construct, constructArgs, method, methodArgs, + * resultAssertions): + * + * Methods of Temporal classes that return a new instance of the same class, + * must not take the constructor of a subclass into account, nor the @@species + * property. This helper runs tests to ensure this. + * + * construct(...constructArgs) must yield a valid instance of the Temporal + * class. instance[method](...methodArgs) is the method call under test, which + * must also yield a valid instance of the same Temporal class, not a + * subclass. See below for the individual tests that this runs. + * resultAssertions() is a function that performs additional assertions on the + * instance returned by the method under test. + */ + checkSubclassingIgnored(...args) { + this.checkSubclassConstructorNotObject(...args); + this.checkSubclassConstructorUndefined(...args); + this.checkSubclassConstructorThrows(...args); + this.checkSubclassConstructorNotCalled(...args); + this.checkSubclassSpeciesInvalidResult(...args); + this.checkSubclassSpeciesNotAConstructor(...args); + this.checkSubclassSpeciesNull(...args); + this.checkSubclassSpeciesUndefined(...args); + this.checkSubclassSpeciesThrows(...args); + }, + + /* + * Checks that replacing the 'constructor' property of the instance with + * various primitive values does not affect the returned new instance. + */ + checkSubclassConstructorNotObject(construct, constructArgs, method, methodArgs, resultAssertions) { + function check(value, description) { + const instance = new construct(...constructArgs); + instance.constructor = value; + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); + resultAssertions(result); + } + + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "Symbol"); + check(7, "number"); + check(7n, "bigint"); + }, + + /* + * Checks that replacing the 'constructor' property of the subclass with + * undefined does not affect the returned new instance. + */ + checkSubclassConstructorUndefined(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + MySubclass.prototype.constructor = undefined; + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Checks that making the 'constructor' property of the instance throw when + * called does not affect the returned new instance. + */ + checkSubclassConstructorThrows(construct, constructArgs, method, methodArgs, resultAssertions) { + function CustomError() {} + const instance = new construct(...constructArgs); + Object.defineProperty(instance, "constructor", { + get() { + throw new CustomError(); + } + }); + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Checks that when subclassing, the subclass constructor is not called by + * the method under test. + */ + checkSubclassConstructorNotCalled(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that the constructor's @@species property is ignored when it's a + * constructor that returns a non-object value. + */ + checkSubclassSpeciesInvalidResult(construct, constructArgs, method, methodArgs, resultAssertions) { + function check(value, description) { + const instance = new construct(...constructArgs); + instance.constructor = { + [Symbol.species]: function() { + return value; + }, + }; + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); + resultAssertions(result); + } + + check(undefined, "undefined"); + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "Symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "plain object"); + }, + + /* + * Check that the constructor's @@species property is ignored when it's not a + * constructor. + */ + checkSubclassSpeciesNotAConstructor(construct, constructArgs, method, methodArgs, resultAssertions) { + function check(value, description) { + const instance = new construct(...constructArgs); + instance.constructor = { + [Symbol.species]: value, + }; + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); + resultAssertions(result); + } + + check(true, "true"); + check("test", "string"); + check(Symbol(), "Symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "plain object"); + }, + + /* + * Check that the constructor's @@species property is ignored when it's null. + */ + checkSubclassSpeciesNull(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + MySubclass.prototype.constructor = { + [Symbol.species]: null, + }; + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that the constructor's @@species property is ignored when it's + * undefined. + */ + checkSubclassSpeciesUndefined(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + MySubclass.prototype.constructor = { + [Symbol.species]: undefined, + }; + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that the constructor's @@species property is ignored when it throws, + * i.e. it is not called at all. + */ + checkSubclassSpeciesThrows(construct, constructArgs, method, methodArgs, resultAssertions) { + function CustomError() {} + + const instance = new construct(...constructArgs); + instance.constructor = { + get [Symbol.species]() { + throw new CustomError(); + }, + }; + + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + }, + + /* + * checkSubclassingIgnoredStatic(construct, method, methodArgs, resultAssertions): + * + * Static methods of Temporal classes that return a new instance of the class, + * must not use the this-value as a constructor. This helper runs tests to + * ensure this. + * + * construct[method](...methodArgs) is the static method call under test, and + * must yield a valid instance of the Temporal class, not a subclass. See + * below for the individual tests that this runs. + * resultAssertions() is a function that performs additional assertions on the + * instance returned by the method under test. + */ + checkSubclassingIgnoredStatic(...args) { + this.checkStaticInvalidReceiver(...args); + this.checkStaticReceiverNotCalled(...args); + this.checkThisValueNotCalled(...args); + }, + + /* + * Check that calling the static method with a receiver that's not callable, + * still calls the intrinsic constructor. + */ + checkStaticInvalidReceiver(construct, method, methodArgs, resultAssertions) { + function check(value, description) { + const result = construct[method].apply(value, methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + } + + check(undefined, "undefined"); + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "Non-callable object"); + }, + + /* + * Check that calling the static method with a receiver that returns a value + * that's not callable, still calls the intrinsic constructor. + */ + checkStaticReceiverNotCalled(construct, method, methodArgs, resultAssertions) { + function check(value, description) { + const receiver = function () { + return value; + }; + const result = construct[method].apply(receiver, methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + } + + check(undefined, "undefined"); + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "Non-callable object"); + }, + + /* + * Check that the receiver isn't called. + */ + checkThisValueNotCalled(construct, method, methodArgs, resultAssertions) { + let called = false; + + class MySubclass extends construct { + constructor(...args) { + called = true; + super(...args); + } + } + + const result = MySubclass[method](...methodArgs); + assert.sameValue(called, false); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that any iterable returned from a custom time zone's + * getPossibleInstantsFor() method is exhausted. + * The custom time zone object is passed in to func(). + * expected is an array of strings representing the expected calls to the + * getPossibleInstantsFor() method. The PlainDateTimes that it is called with, + * are compared (using their toString() results) with the array. + */ + checkTimeZonePossibleInstantsIterable(func, expected) { + // A custom time zone that returns an iterable instead of an array from its + // getPossibleInstantsFor() method, and for testing purposes skips + // 00:00-01:00 UTC on January 1, 2030, and repeats 00:00-01:00 UTC+1 on + // January 3, 2030. Otherwise identical to the UTC time zone. + class TimeZonePossibleInstantsIterable extends Temporal.TimeZone { + constructor() { + super("UTC"); + this.getPossibleInstantsForCallCount = 0; + this.getPossibleInstantsForCalledWith = []; + this.getPossibleInstantsForReturns = []; + this.iteratorExhausted = []; + } + + toString() { + return "Custom/Iterable"; + } + + getOffsetNanosecondsFor(instant) { + if (Temporal.Instant.compare(instant, "2030-01-01T00:00Z") >= 0 && + Temporal.Instant.compare(instant, "2030-01-03T01:00Z") < 0) { + return 3600_000_000_000; + } else { + return 0; + } + } + + getPossibleInstantsFor(dateTime) { + this.getPossibleInstantsForCallCount++; + this.getPossibleInstantsForCalledWith.push(dateTime); + + // Fake DST transition + let retval = super.getPossibleInstantsFor(dateTime); + if (dateTime.toPlainDate().equals("2030-01-01") && dateTime.hour === 0) { + retval = []; + } else if (dateTime.toPlainDate().equals("2030-01-03") && dateTime.hour === 0) { + retval.push(retval[0].subtract({ hours: 1 })); + } else if (dateTime.year === 2030 && dateTime.month === 1 && dateTime.day >= 1 && dateTime.day <= 2) { + retval[0] = retval[0].subtract({ hours: 1 }); + } + + this.getPossibleInstantsForReturns.push(retval); + this.iteratorExhausted.push(false); + return { + callIndex: this.getPossibleInstantsForCallCount - 1, + timeZone: this, + *[Symbol.iterator]() { + yield* this.timeZone.getPossibleInstantsForReturns[this.callIndex]; + this.timeZone.iteratorExhausted[this.callIndex] = true; + }, + }; + } + } + + const timeZone = new TimeZonePossibleInstantsIterable(); + func(timeZone); + + assert.sameValue(timeZone.getPossibleInstantsForCallCount, expected.length, "getPossibleInstantsFor() method called correct number of times"); + + for (let index = 0; index < expected.length; index++) { + assert.sameValue(timeZone.getPossibleInstantsForCalledWith[index].toString(), expected[index], "getPossibleInstantsFor() called with expected PlainDateTime"); + assert(timeZone.iteratorExhausted[index], "iterated through the whole iterable"); + } + }, + + /* + * Check that any calendar-carrying Temporal object has its [[Calendar]] + * internal slot read by ToTemporalCalendar, and does not fetch the calendar + * by calling getters. + * The custom calendar object is passed in to func() so that it can do its + * own additional assertions involving the calendar if necessary. (Sometimes + * there is nothing to assert as the calendar isn't stored anywhere that can + * be asserted about.) + */ + checkToTemporalCalendarFastPath(func) { + class CalendarFastPathCheck extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateFromFields(...args) { + return super.dateFromFields(...args).withCalendar(this); + } + + monthDayFromFields(...args) { + const { isoYear, isoMonth, isoDay } = super.monthDayFromFields(...args).getISOFields(); + return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); + } + + yearMonthFromFields(...args) { + const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields(...args).getISOFields(); + return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); + } + + toString() { + return "fast-path-check"; + } + } + const calendar = new CalendarFastPathCheck(); + + const plainDate = new Temporal.PlainDate(2000, 5, 2, calendar); + const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar); + const plainMonthDay = new Temporal.PlainMonthDay(5, 2, calendar); + const plainYearMonth = new Temporal.PlainYearMonth(2000, 5, calendar); + const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + + [plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((temporalObject) => { + const actual = []; + const expected = []; + + Object.defineProperty(temporalObject, "calendar", { + get() { + actual.push("get calendar"); + return calendar; + }, + }); + + func(temporalObject, calendar); + assert.compareArray(actual, expected, "calendar getter not called"); + }); + }, + + checkToTemporalInstantFastPath(func) { + const actual = []; + const expected = []; + + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + Object.defineProperty(datetime, 'toString', { + get() { + actual.push("get toString"); + return function (options) { + actual.push("call toString"); + return Temporal.ZonedDateTime.prototype.toString.call(this, options); + }; + }, + }); + + func(datetime); + assert.compareArray(actual, expected, "toString not called"); + }, + + checkToTemporalPlainDateTimeFastPath(func) { + const actual = []; + const expected = []; + + const calendar = new Temporal.Calendar("iso8601"); + const date = new Temporal.PlainDate(2000, 5, 2, calendar); + const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDate.prototype); + ["year", "month", "monthCode", "day"].forEach((property) => { + Object.defineProperty(date, property, { + get() { + actual.push(`get ${formatPropertyName(property)}`); + const value = prototypeDescrs[property].get.call(this); + return TemporalHelpers.toPrimitiveObserver(actual, value, property); + }, + }); + }); + ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => { + Object.defineProperty(date, property, { + get() { + actual.push(`get ${formatPropertyName(property)}`); + return undefined; + }, + }); + }); + Object.defineProperty(date, "calendar", { + get() { + actual.push("get calendar"); + return calendar; + }, + }); + + func(date, calendar); + assert.compareArray(actual, expected, "property getters not called"); + }, + + /* + * A custom calendar used in prototype pollution checks. Verifies that the + * fromFields methods are always called with a null-prototype fields object. + */ + calendarCheckFieldsPrototypePollution() { + class CalendarCheckFieldsPrototypePollution extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateFromFieldsCallCount = 0; + this.yearMonthFromFieldsCallCount = 0; + this.monthDayFromFieldsCallCount = 0; + } + + // toString must remain "iso8601", so that some methods don't throw due to + // incompatible calendars + + dateFromFields(fields, options = {}) { + this.dateFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "dateFromFields should be called with null-prototype fields object"); + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options = {}) { + this.yearMonthFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "yearMonthFromFields should be called with null-prototype fields object"); + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options = {}) { + this.monthDayFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "monthDayFromFields should be called with null-prototype fields object"); + return super.monthDayFromFields(fields, options); + } + } + + return new CalendarCheckFieldsPrototypePollution(); + }, + + /* + * A custom calendar used in prototype pollution checks. Verifies that the + * mergeFields() method is always called with null-prototype fields objects. + */ + calendarCheckMergeFieldsPrototypePollution() { + class CalendarCheckMergeFieldsPrototypePollution extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.mergeFieldsCallCount = 0; + } + + toString() { + return "merge-fields-null-proto"; + } + + mergeFields(fields, additionalFields) { + this.mergeFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "mergeFields should be called with null-prototype fields object (first argument)"); + assert.sameValue(Object.getPrototypeOf(additionalFields), null, "mergeFields should be called with null-prototype fields object (second argument)"); + return super.mergeFields(fields, additionalFields); + } + } + + return new CalendarCheckMergeFieldsPrototypePollution(); + }, + + /* + * A custom calendar used in prototype pollution checks. Verifies that methods + * are always called with a null-prototype options object. + */ + calendarCheckOptionsPrototypePollution() { + class CalendarCheckOptionsPrototypePollution extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.yearMonthFromFieldsCallCount = 0; + this.dateUntilCallCount = 0; + } + + toString() { + return "options-null-proto"; + } + + yearMonthFromFields(fields, options) { + this.yearMonthFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(options), null, "yearMonthFromFields should be called with null-prototype options"); + return super.yearMonthFromFields(fields, options); + } + + dateUntil(one, two, options) { + this.dateUntilCallCount++; + assert.sameValue(Object.getPrototypeOf(options), null, "dateUntil should be called with null-prototype options"); + return super.dateUntil(one, two, options); + } + } + + return new CalendarCheckOptionsPrototypePollution(); + }, + + /* + * A custom calendar that asserts its dateAdd() method is called with the + * options parameter having the value undefined. + */ + calendarDateAddUndefinedOptions() { + class CalendarDateAddUndefinedOptions extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateAddCallCount = 0; + } + + toString() { + return "dateadd-undef-options"; + } + + dateAdd(date, duration, options) { + this.dateAddCallCount++; + assert.sameValue(options, undefined, "dateAdd shouldn't be called with options"); + return super.dateAdd(date, duration, options); + } + } + return new CalendarDateAddUndefinedOptions(); + }, + + /* + * A custom calendar that asserts its dateAdd() method is called with a + * PlainDate instance. Optionally, it also asserts that the PlainDate instance + * is the specific object `this.specificPlainDate`, if it is set by the + * calling code. + */ + calendarDateAddPlainDateInstance() { + class CalendarDateAddPlainDateInstance extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateAddCallCount = 0; + this.specificPlainDate = undefined; + } + + toString() { + return "dateadd-plain-date-instance"; + } + + dateFromFields(...args) { + return super.dateFromFields(...args).withCalendar(this); + } + + dateAdd(date, duration, options) { + this.dateAddCallCount++; + assert(date instanceof Temporal.PlainDate, "dateAdd() should be called with a PlainDate instance"); + if (this.dateAddCallCount === 1 && this.specificPlainDate) { + assert.sameValue(date, this.specificPlainDate, `dateAdd() should be called first with the specific PlainDate instance ${this.specificPlainDate}`); + } + return super.dateAdd(date, duration, options).withCalendar(this); + } + } + return new CalendarDateAddPlainDateInstance(); + }, + + /* + * A custom calendar that returns an iterable instead of an array from its + * fields() method, otherwise identical to the ISO calendar. + */ + calendarFieldsIterable() { + class CalendarFieldsIterable extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.fieldsCallCount = 0; + this.fieldsCalledWith = []; + this.iteratorExhausted = []; + } + + toString() { + return "fields-iterable"; + } + + fields(fieldNames) { + this.fieldsCallCount++; + this.fieldsCalledWith.push(fieldNames.slice()); + this.iteratorExhausted.push(false); + return { + callIndex: this.fieldsCallCount - 1, + calendar: this, + *[Symbol.iterator]() { + yield* this.calendar.fieldsCalledWith[this.callIndex]; + this.calendar.iteratorExhausted[this.callIndex] = true; + }, + }; + } + } + return new CalendarFieldsIterable(); + }, + + /* + * A custom calendar that asserts its ...FromFields() methods are called with + * the options parameter having the value undefined. + */ + calendarFromFieldsUndefinedOptions() { + class CalendarFromFieldsUndefinedOptions extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateFromFieldsCallCount = 0; + this.monthDayFromFieldsCallCount = 0; + this.yearMonthFromFieldsCallCount = 0; + } + + toString() { + return "from-fields-undef-options"; + } + + dateFromFields(fields, options) { + this.dateFromFieldsCallCount++; + assert.sameValue(options, undefined, "dateFromFields shouldn't be called with options"); + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options) { + this.yearMonthFromFieldsCallCount++; + assert.sameValue(options, undefined, "yearMonthFromFields shouldn't be called with options"); + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options) { + this.monthDayFromFieldsCallCount++; + assert.sameValue(options, undefined, "monthDayFromFields shouldn't be called with options"); + return super.monthDayFromFields(fields, options); + } + } + return new CalendarFromFieldsUndefinedOptions(); + }, + + /* + * A custom calendar that modifies the fields object passed in to + * dateFromFields, sabotaging its time properties. + */ + calendarMakeInfinityTime() { + class CalendarMakeInfinityTime extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateFromFields(fields, options) { + const retval = super.dateFromFields(fields, options); + fields.hour = Infinity; + fields.minute = Infinity; + fields.second = Infinity; + fields.millisecond = Infinity; + fields.microsecond = Infinity; + fields.nanosecond = Infinity; + return retval; + } + } + return new CalendarMakeInfinityTime(); + }, + + /* + * A custom calendar that defines getters on the fields object passed into + * dateFromFields that throw, sabotaging its time properties. + */ + calendarMakeInvalidGettersTime() { + class CalendarMakeInvalidGettersTime extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateFromFields(fields, options) { + const retval = super.dateFromFields(fields, options); + const throwingDescriptor = { + get() { + throw new Test262Error("reading a sabotaged time field"); + }, + }; + Object.defineProperties(fields, { + hour: throwingDescriptor, + minute: throwingDescriptor, + second: throwingDescriptor, + millisecond: throwingDescriptor, + microsecond: throwingDescriptor, + nanosecond: throwingDescriptor, + }); + return retval; + } + } + return new CalendarMakeInvalidGettersTime(); + }, + + /* + * A custom calendar whose mergeFields() method returns a proxy object with + * all of its Get and HasProperty operations observable, as well as adding a + * "shouldNotBeCopied": true property. + */ + calendarMergeFieldsGetters() { + class CalendarMergeFieldsGetters extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.mergeFieldsReturnOperations = []; + } + + toString() { + return "merge-fields-getters"; + } + + dateFromFields(fields, options) { + assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied"); + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options) { + assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied"); + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options) { + assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied"); + return super.monthDayFromFields(fields, options); + } + + mergeFields(fields, additionalFields) { + const retval = super.mergeFields(fields, additionalFields); + retval._calendar = this; + retval.shouldNotBeCopied = true; + return new Proxy(retval, { + get(target, key) { + target._calendar.mergeFieldsReturnOperations.push(`get ${key}`); + const result = target[key]; + if (result === undefined) { + return undefined; + } + return TemporalHelpers.toPrimitiveObserver(target._calendar.mergeFieldsReturnOperations, result, key); + }, + has(target, key) { + target._calendar.mergeFieldsReturnOperations.push(`has ${key}`); + return key in target; + }, + }); + } + } + return new CalendarMergeFieldsGetters(); + }, + + /* + * A custom calendar whose mergeFields() method returns a primitive value, + * given by @primitive, and which records the number of calls made to its + * dateFromFields(), yearMonthFromFields(), and monthDayFromFields() methods. + */ + calendarMergeFieldsReturnsPrimitive(primitive) { + class CalendarMergeFieldsPrimitive extends Temporal.Calendar { + constructor(mergeFieldsReturnValue) { + super("iso8601"); + this._mergeFieldsReturnValue = mergeFieldsReturnValue; + this.dateFromFieldsCallCount = 0; + this.monthDayFromFieldsCallCount = 0; + this.yearMonthFromFieldsCallCount = 0; + } + + toString() { + return "merge-fields-primitive"; + } + + dateFromFields(fields, options) { + this.dateFromFieldsCallCount++; + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options) { + this.yearMonthFromFieldsCallCount++; + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options) { + this.monthDayFromFieldsCallCount++; + return super.monthDayFromFields(fields, options); + } + + mergeFields() { + return this._mergeFieldsReturnValue; + } + } + return new CalendarMergeFieldsPrimitive(primitive); + }, + + /* + * A custom calendar whose fields() method returns the same value as the + * iso8601 calendar, with the addition of extraFields provided as parameter. + */ + calendarWithExtraFields(fields) { + class CalendarWithExtraFields extends Temporal.Calendar { + constructor(extraFields) { + super("iso8601"); + this._extraFields = extraFields; + } + + fields(fieldNames) { + return super.fields(fieldNames).concat(this._extraFields); + } + } + + return new CalendarWithExtraFields(fields); + }, + + /* + * crossDateLineTimeZone(): + * + * This returns an instance of a custom time zone class that implements one + * single transition where the time zone moves from one side of the + * International Date Line to the other, for the purpose of testing time zone + * calculations without depending on system time zone data. + * + * The transition occurs at epoch second 1325239200 and goes from offset + * -10:00 to +14:00. In other words, the time zone skips the whole calendar + * day of 2011-12-30. This is the same as the real-life transition in the + * Pacific/Apia time zone. + */ + crossDateLineTimeZone() { + const { compare } = Temporal.PlainDate; + const skippedDay = new Temporal.PlainDate(2011, 12, 30); + const transitionEpoch = 1325239200_000_000_000n; + const beforeOffset = new Temporal.TimeZone("-10:00"); + const afterOffset = new Temporal.TimeZone("+14:00"); + + class CrossDateLineTimeZone extends Temporal.TimeZone { + constructor() { + super("+14:00"); + } + + getOffsetNanosecondsFor(instant) { + if (instant.epochNanoseconds < transitionEpoch) { + return beforeOffset.getOffsetNanosecondsFor(instant); + } + return afterOffset.getOffsetNanosecondsFor(instant); + } + + getPossibleInstantsFor(datetime) { + const comparison = compare(datetime.toPlainDate(), skippedDay); + if (comparison === 0) { + return []; + } + if (comparison < 0) { + return [beforeOffset.getInstantFor(datetime)]; + } + return [afterOffset.getInstantFor(datetime)]; + } + + getPreviousTransition(instant) { + if (instant.epochNanoseconds > transitionEpoch) return new Temporal.Instant(transitionEpoch); + return null; + } + + getNextTransition(instant) { + if (instant.epochNanoseconds < transitionEpoch) return new Temporal.Instant(transitionEpoch); + return null; + } + + toString() { + return "Custom/Date_Line"; + } + } + return new CrossDateLineTimeZone(); + }, + + /* + * observeProperty(calls, object, propertyName, value): + * + * Defines an own property @object.@propertyName with value @value, that + * will log any calls to its accessors to the array @calls. + */ + observeProperty(calls, object, propertyName, value, objectName = "") { + Object.defineProperty(object, propertyName, { + get() { + calls.push(`get ${formatPropertyName(propertyName, objectName)}`); + return value; + }, + set(v) { + calls.push(`set ${formatPropertyName(propertyName, objectName)}`); + } + }); + }, + + /* + * observeMethod(calls, object, propertyName, value): + * + * Defines an own property @object.@propertyName with value @value, that + * will log any calls of @value to the array @calls. + */ + observeMethod(calls, object, propertyName, objectName = "") { + const method = object[propertyName]; + object[propertyName] = function () { + calls.push(`call ${formatPropertyName(propertyName, objectName)}`); + return method.apply(object, arguments); + }; + }, + + /* + * Used for substituteMethod to indicate default behavior instead of a + * substituted value + */ + SUBSTITUTE_SKIP: SKIP_SYMBOL, + + /* + * substituteMethod(object, propertyName, values): + * + * Defines an own property @object.@propertyName that will, for each + * subsequent call to the method previously defined as + * @object.@propertyName: + * - Call the method, if no more values remain + * - Call the method, if the value in @values for the corresponding call + * is SUBSTITUTE_SKIP + * - Otherwise, return the corresponding value in @value + */ + substituteMethod(object, propertyName, values) { + let calls = 0; + const method = object[propertyName]; + object[propertyName] = function () { + if (calls >= values.length) { + return method.apply(object, arguments); + } else if (values[calls] === SKIP_SYMBOL) { + calls++; + return method.apply(object, arguments); + } else { + return values[calls++]; + } + }; + }, + + /* + * calendarObserver: + * A custom calendar that behaves exactly like the ISO 8601 calendar but + * tracks calls to any of its methods, and Get/Has operations on its + * properties, by appending messages to an array. This is for the purpose of + * testing order of operations that are observable from user code. + * objectName is used in the log. + */ + calendarObserver(calls, objectName, methodOverrides = {}) { + function removeExtraHasPropertyChecks(objectName, calls) { + // Inserting the tracking calendar into the return values of methods + // that we chain up into the ISO calendar for, causes extra HasProperty + // checks, which we observe. This removes them so that we don't leak + // implementation details of the helper into the test code. + assert.sameValue(calls.pop(), `has ${objectName}.yearOfWeek`); + assert.sameValue(calls.pop(), `has ${objectName}.yearMonthFromFields`); + assert.sameValue(calls.pop(), `has ${objectName}.year`); + assert.sameValue(calls.pop(), `has ${objectName}.weekOfYear`); + assert.sameValue(calls.pop(), `has ${objectName}.monthsInYear`); + assert.sameValue(calls.pop(), `has ${objectName}.monthDayFromFields`); + assert.sameValue(calls.pop(), `has ${objectName}.monthCode`); + assert.sameValue(calls.pop(), `has ${objectName}.month`); + assert.sameValue(calls.pop(), `has ${objectName}.mergeFields`); + assert.sameValue(calls.pop(), `has ${objectName}.inLeapYear`); + assert.sameValue(calls.pop(), `has ${objectName}.id`); + assert.sameValue(calls.pop(), `has ${objectName}.fields`); + assert.sameValue(calls.pop(), `has ${objectName}.daysInYear`); + assert.sameValue(calls.pop(), `has ${objectName}.daysInWeek`); + assert.sameValue(calls.pop(), `has ${objectName}.daysInMonth`); + assert.sameValue(calls.pop(), `has ${objectName}.dayOfYear`); + assert.sameValue(calls.pop(), `has ${objectName}.dayOfWeek`); + assert.sameValue(calls.pop(), `has ${objectName}.day`); + assert.sameValue(calls.pop(), `has ${objectName}.dateUntil`); + assert.sameValue(calls.pop(), `has ${objectName}.dateFromFields`); + assert.sameValue(calls.pop(), `has ${objectName}.dateAdd`); + } + + const iso8601 = new Temporal.Calendar("iso8601"); + const trackingMethods = { + dateFromFields(...args) { + calls.push(`call ${objectName}.dateFromFields`); + if ('dateFromFields' in methodOverrides) { + const value = methodOverrides.dateFromFields; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.dateFromFields(...args); + // Replace the calendar in the result with the call-tracking calendar + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + yearMonthFromFields(...args) { + calls.push(`call ${objectName}.yearMonthFromFields`); + if ('yearMonthFromFields' in methodOverrides) { + const value = methodOverrides.yearMonthFromFields; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.yearMonthFromFields(...args); + // Replace the calendar in the result with the call-tracking calendar + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + monthDayFromFields(...args) { + calls.push(`call ${objectName}.monthDayFromFields`); + if ('monthDayFromFields' in methodOverrides) { + const value = methodOverrides.monthDayFromFields; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.monthDayFromFields(...args); + // Replace the calendar in the result with the call-tracking calendar + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + dateAdd(...args) { + calls.push(`call ${objectName}.dateAdd`); + if ('dateAdd' in methodOverrides) { + const value = methodOverrides.dateAdd; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.dateAdd(...args); + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + id: "iso8601", + }; + // Automatically generate the other methods that don't need any custom code + [ + "dateUntil", + "day", + "dayOfWeek", + "dayOfYear", + "daysInMonth", + "daysInWeek", + "daysInYear", + "era", + "eraYear", + "fields", + "inLeapYear", + "mergeFields", + "month", + "monthCode", + "monthsInYear", + "toString", + "weekOfYear", + "year", + "yearOfWeek", + ].forEach((methodName) => { + trackingMethods[methodName] = function (...args) { + calls.push(`call ${formatPropertyName(methodName, objectName)}`); + if (methodName in methodOverrides) { + const value = methodOverrides[methodName]; + return typeof value === "function" ? value(...args) : value; + } + return iso8601[methodName](...args); + }; + }); + return new Proxy(trackingMethods, { + get(target, key, receiver) { + const result = Reflect.get(target, key, receiver); + calls.push(`get ${formatPropertyName(key, objectName)}`); + return result; + }, + has(target, key) { + calls.push(`has ${formatPropertyName(key, objectName)}`); + return Reflect.has(target, key); + }, + }); + }, + + /* + * A custom calendar that does not allow any of its methods to be called, for + * the purpose of asserting that a particular operation does not call into + * user code. + */ + calendarThrowEverything() { + class CalendarThrowEverything extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + toString() { + TemporalHelpers.assertUnreachable("toString should not be called"); + } + dateFromFields() { + TemporalHelpers.assertUnreachable("dateFromFields should not be called"); + } + yearMonthFromFields() { + TemporalHelpers.assertUnreachable("yearMonthFromFields should not be called"); + } + monthDayFromFields() { + TemporalHelpers.assertUnreachable("monthDayFromFields should not be called"); + } + dateAdd() { + TemporalHelpers.assertUnreachable("dateAdd should not be called"); + } + dateUntil() { + TemporalHelpers.assertUnreachable("dateUntil should not be called"); + } + era() { + TemporalHelpers.assertUnreachable("era should not be called"); + } + eraYear() { + TemporalHelpers.assertUnreachable("eraYear should not be called"); + } + year() { + TemporalHelpers.assertUnreachable("year should not be called"); + } + month() { + TemporalHelpers.assertUnreachable("month should not be called"); + } + monthCode() { + TemporalHelpers.assertUnreachable("monthCode should not be called"); + } + day() { + TemporalHelpers.assertUnreachable("day should not be called"); + } + fields() { + TemporalHelpers.assertUnreachable("fields should not be called"); + } + mergeFields() { + TemporalHelpers.assertUnreachable("mergeFields should not be called"); + } + } + + return new CalendarThrowEverything(); + }, + + /* + * oneShiftTimeZone(shiftInstant, shiftNanoseconds): + * + * In the case of a spring-forward time zone offset transition (skipped time), + * and disambiguation === 'earlier', BuiltinTimeZoneGetInstantFor subtracts a + * negative number of nanoseconds from a PlainDateTime, which should balance + * with the microseconds field. + * + * This returns an instance of a custom time zone class which skips a length + * of time equal to shiftNanoseconds (a number), at the Temporal.Instant + * shiftInstant. Before shiftInstant, it's identical to UTC, and after + * shiftInstant it's a constant-offset time zone. + * + * It provides a getPossibleInstantsForCalledWith member which is an array + * with the result of calling toString() on any PlainDateTimes passed to + * getPossibleInstantsFor(). + */ + oneShiftTimeZone(shiftInstant, shiftNanoseconds) { + class OneShiftTimeZone extends Temporal.TimeZone { + constructor(shiftInstant, shiftNanoseconds) { + super("+00:00"); + this._shiftInstant = shiftInstant; + this._epoch1 = shiftInstant.epochNanoseconds; + this._epoch2 = this._epoch1 + BigInt(shiftNanoseconds); + this._shiftNanoseconds = shiftNanoseconds; + this._shift = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, this._shiftNanoseconds); + this.getPossibleInstantsForCalledWith = []; + } + + _isBeforeShift(instant) { + return instant.epochNanoseconds < this._epoch1; + } + + getOffsetNanosecondsFor(instant) { + return this._isBeforeShift(instant) ? 0 : this._shiftNanoseconds; + } + + getPossibleInstantsFor(plainDateTime) { + this.getPossibleInstantsForCalledWith.push(plainDateTime.toString({ calendarName: "never" })); + const [instant] = super.getPossibleInstantsFor(plainDateTime); + if (this._shiftNanoseconds > 0) { + if (this._isBeforeShift(instant)) return [instant]; + if (instant.epochNanoseconds < this._epoch2) return []; + return [instant.subtract(this._shift)]; + } + if (instant.epochNanoseconds < this._epoch2) return [instant]; + const shifted = instant.subtract(this._shift); + if (this._isBeforeShift(instant)) return [instant, shifted]; + return [shifted]; + } + + getNextTransition(instant) { + return this._isBeforeShift(instant) ? this._shiftInstant : null; + } + + getPreviousTransition(instant) { + return this._isBeforeShift(instant) ? null : this._shiftInstant; + } + + toString() { + return "Custom/One_Shift"; + } + } + return new OneShiftTimeZone(shiftInstant, shiftNanoseconds); + }, + + /* + * propertyBagObserver(): + * Returns an object that behaves like the given propertyBag but tracks Get + * and Has operations on any of its properties, by appending messages to an + * array. If the value of a property in propertyBag is a primitive, the value + * of the returned object's property will additionally be a + * TemporalHelpers.toPrimitiveObserver that will track calls to its toString + * and valueOf methods in the same array. This is for the purpose of testing + * order of operations that are observable from user code. objectName is used + * in the log. + */ + propertyBagObserver(calls, propertyBag, objectName) { + return new Proxy(propertyBag, { + ownKeys(target) { + calls.push(`ownKeys ${objectName}`); + return Reflect.ownKeys(target); + }, + getOwnPropertyDescriptor(target, key) { + calls.push(`getOwnPropertyDescriptor ${formatPropertyName(key, objectName)}`); + return Reflect.getOwnPropertyDescriptor(target, key); + }, + get(target, key, receiver) { + calls.push(`get ${formatPropertyName(key, objectName)}`); + const result = Reflect.get(target, key, receiver); + if (result === undefined) { + return undefined; + } + if ((result !== null && typeof result === "object") || typeof result === "function") { + return result; + } + return TemporalHelpers.toPrimitiveObserver(calls, result, `${formatPropertyName(key, objectName)}`); + }, + has(target, key) { + calls.push(`has ${formatPropertyName(key, objectName)}`); + return Reflect.has(target, key); + }, + }); + }, + + /* + * specificOffsetTimeZone(): + * + * This returns an instance of a custom time zone class, which returns a + * specific custom value from its getOffsetNanosecondsFrom() method. This is + * for the purpose of testing the validation of what this method returns. + * + * It also returns an empty array from getPossibleInstantsFor(), so as to + * trigger calls to getOffsetNanosecondsFor() when used from the + * BuiltinTimeZoneGetInstantFor operation. + */ + specificOffsetTimeZone(offsetValue) { + class SpecificOffsetTimeZone extends Temporal.TimeZone { + constructor(offsetValue) { + super("UTC"); + this._offsetValue = offsetValue; + } + + getOffsetNanosecondsFor() { + return this._offsetValue; + } + + getPossibleInstantsFor(dt) { + if (typeof this._offsetValue !== 'number' || Math.abs(this._offsetValue) >= 86400e9 || isNaN(this._offsetValue)) return []; + const zdt = dt.toZonedDateTime("UTC").add({ nanoseconds: -this._offsetValue }); + return [zdt.toInstant()]; + } + + get id() { + return this.getOffsetStringFor(new Temporal.Instant(0n)); + } + } + return new SpecificOffsetTimeZone(offsetValue); + }, + + /* + * springForwardFallBackTimeZone(): + * + * This returns an instance of a custom time zone class that implements one + * single spring-forward/fall-back transition, for the purpose of testing the + * disambiguation option, without depending on system time zone data. + * + * The spring-forward occurs at epoch second 954669600 (2000-04-02T02:00 + * local) and goes from offset -08:00 to -07:00. + * + * The fall-back occurs at epoch second 972810000 (2000-10-29T02:00 local) and + * goes from offset -07:00 to -08:00. + */ + springForwardFallBackTimeZone() { + const { compare } = Temporal.PlainDateTime; + const springForwardLocal = new Temporal.PlainDateTime(2000, 4, 2, 2); + const springForwardEpoch = 954669600_000_000_000n; + const fallBackLocal = new Temporal.PlainDateTime(2000, 10, 29, 1); + const fallBackEpoch = 972810000_000_000_000n; + const winterOffset = new Temporal.TimeZone('-08:00'); + const summerOffset = new Temporal.TimeZone('-07:00'); + + class SpringForwardFallBackTimeZone extends Temporal.TimeZone { + constructor() { + super("-08:00"); + } + + getOffsetNanosecondsFor(instant) { + if (instant.epochNanoseconds < springForwardEpoch || + instant.epochNanoseconds >= fallBackEpoch) { + return winterOffset.getOffsetNanosecondsFor(instant); + } + return summerOffset.getOffsetNanosecondsFor(instant); + } + + getPossibleInstantsFor(datetime) { + if (compare(datetime, springForwardLocal) >= 0 && compare(datetime, springForwardLocal.add({ hours: 1 })) < 0) { + return []; + } + if (compare(datetime, fallBackLocal) >= 0 && compare(datetime, fallBackLocal.add({ hours: 1 })) < 0) { + return [summerOffset.getInstantFor(datetime), winterOffset.getInstantFor(datetime)]; + } + if (compare(datetime, springForwardLocal) < 0 || compare(datetime, fallBackLocal) >= 0) { + return [winterOffset.getInstantFor(datetime)]; + } + return [summerOffset.getInstantFor(datetime)]; + } + + getPreviousTransition(instant) { + if (instant.epochNanoseconds > fallBackEpoch) return new Temporal.Instant(fallBackEpoch); + if (instant.epochNanoseconds > springForwardEpoch) return new Temporal.Instant(springForwardEpoch); + return null; + } + + getNextTransition(instant) { + if (instant.epochNanoseconds < springForwardEpoch) return new Temporal.Instant(springForwardEpoch); + if (instant.epochNanoseconds < fallBackEpoch) return new Temporal.Instant(fallBackEpoch); + return null; + } + + get id() { + return "Custom/Spring_Fall"; + } + + toString() { + return "Custom/Spring_Fall"; + } + } + return new SpringForwardFallBackTimeZone(); + }, + + /* + * timeZoneObserver: + * A custom calendar that behaves exactly like the UTC time zone but tracks + * calls to any of its methods, and Get/Has operations on its properties, by + * appending messages to an array. This is for the purpose of testing order of + * operations that are observable from user code. objectName is used in the + * log. methodOverrides is an optional object containing properties with the + * same name as Temporal.TimeZone methods. If the property value is a function + * it will be called with the proper arguments instead of the UTC method. + * Otherwise, the property value will be returned directly. + */ + timeZoneObserver(calls, objectName, methodOverrides = {}) { + const utc = new Temporal.TimeZone("UTC"); + const trackingMethods = { + id: "UTC", + }; + // Automatically generate the methods + ["getOffsetNanosecondsFor", "getPossibleInstantsFor", "toString"].forEach((methodName) => { + trackingMethods[methodName] = function (...args) { + calls.push(`call ${formatPropertyName(methodName, objectName)}`); + if (methodName in methodOverrides) { + const value = methodOverrides[methodName]; + return typeof value === "function" ? value(...args) : value; + } + return utc[methodName](...args); + }; + }); + return new Proxy(trackingMethods, { + get(target, key, receiver) { + const result = Reflect.get(target, key, receiver); + calls.push(`get ${formatPropertyName(key, objectName)}`); + return result; + }, + has(target, key) { + calls.push(`has ${formatPropertyName(key, objectName)}`); + return Reflect.has(target, key); + }, + }); + }, + + /* + * A custom time zone that does not allow any of its methods to be called, for + * the purpose of asserting that a particular operation does not call into + * user code. + */ + timeZoneThrowEverything() { + class TimeZoneThrowEverything extends Temporal.TimeZone { + constructor() { + super("UTC"); + } + getOffsetNanosecondsFor() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be called"); + } + getPossibleInstantsFor() { + TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be called"); + } + toString() { + TemporalHelpers.assertUnreachable("toString should not be called"); + } + } + + return new TimeZoneThrowEverything(); + }, + + /* + * Returns an object that will append logs of any Gets or Calls of its valueOf + * or toString properties to the array calls. Both valueOf and toString will + * return the actual primitiveValue. propertyName is used in the log. + */ + toPrimitiveObserver(calls, primitiveValue, propertyName) { + return { + get valueOf() { + calls.push(`get ${propertyName}.valueOf`); + return function () { + calls.push(`call ${propertyName}.valueOf`); + return primitiveValue; + }; + }, + get toString() { + calls.push(`get ${propertyName}.toString`); + return function () { + calls.push(`call ${propertyName}.toString`); + if (primitiveValue === undefined) return undefined; + return primitiveValue.toString(); + }; + }, + }; + }, + + /* + * An object containing further methods that return arrays of ISO strings, for + * testing parsers. + */ + ISO: { + /* + * PlainMonthDay strings that are not valid. + */ + plainMonthDayStringsInvalid() { + return [ + "11-18junk", + "11-18[u-ca=gregory]", + "11-18[u-ca=hebrew]", + ]; + }, + + /* + * PlainMonthDay strings that are valid and that should produce October 1st. + */ + plainMonthDayStringsValid() { + return [ + "10-01", + "1001", + "1965-10-01", + "1976-10-01T152330.1+00:00", + "19761001T15:23:30.1+00:00", + "1976-10-01T15:23:30.1+0000", + "1976-10-01T152330.1+0000", + "19761001T15:23:30.1+0000", + "19761001T152330.1+00:00", + "19761001T152330.1+0000", + "+001976-10-01T152330.1+00:00", + "+0019761001T15:23:30.1+00:00", + "+001976-10-01T15:23:30.1+0000", + "+001976-10-01T152330.1+0000", + "+0019761001T15:23:30.1+0000", + "+0019761001T152330.1+00:00", + "+0019761001T152330.1+0000", + "1976-10-01T15:23:00", + "1976-10-01T15:23", + "1976-10-01T15", + "1976-10-01", + "--10-01", + "--1001", + ]; + }, + + /* + * PlainTime strings that may be mistaken for PlainMonthDay or + * PlainYearMonth strings, and so require a time designator. + */ + plainTimeStringsAmbiguous() { + const ambiguousStrings = [ + "2021-12", // ambiguity between YYYY-MM and HHMM-UU + "2021-12[-12:00]", // ditto, TZ does not disambiguate + "1214", // ambiguity between MMDD and HHMM + "0229", // ditto, including MMDD that doesn't occur every year + "1130", // ditto, including DD that doesn't occur in every month + "12-14", // ambiguity between MM-DD and HH-UU + "12-14[-14:00]", // ditto, TZ does not disambiguate + "202112", // ambiguity between YYYYMM and HHMMSS + "202112[UTC]", // ditto, TZ does not disambiguate + ]; + // Adding a calendar annotation to one of these strings must not cause + // disambiguation in favour of time. + const stringsWithCalendar = ambiguousStrings.map((s) => s + '[u-ca=iso8601]'); + return ambiguousStrings.concat(stringsWithCalendar); + }, + + /* + * PlainTime strings that are of similar form to PlainMonthDay and + * PlainYearMonth strings, but are not ambiguous due to components that + * aren't valid as months or days. + */ + plainTimeStringsUnambiguous() { + return [ + "2021-13", // 13 is not a month + "202113", // ditto + "2021-13[-13:00]", // ditto + "202113[-13:00]", // ditto + "0000-00", // 0 is not a month + "000000", // ditto + "0000-00[UTC]", // ditto + "000000[UTC]", // ditto + "1314", // 13 is not a month + "13-14", // ditto + "1232", // 32 is not a day + "0230", // 30 is not a day in February + "0631", // 31 is not a day in June + "0000", // 0 is neither a month nor a day + "00-00", // ditto + ]; + }, + + /* + * PlainYearMonth-like strings that are not valid. + */ + plainYearMonthStringsInvalid() { + return [ + "2020-13", + ]; + }, + + /* + * PlainYearMonth-like strings that are valid and should produce November + * 1976 in the ISO 8601 calendar. + */ + plainYearMonthStringsValid() { + return [ + "1976-11", + "1976-11-10", + "1976-11-01T09:00:00+00:00", + "1976-11-01T00:00:00+05:00", + "197611", + "+00197611", + "1976-11-18T15:23:30.1\u221202:00", + "1976-11-18T152330.1+00:00", + "19761118T15:23:30.1+00:00", + "1976-11-18T15:23:30.1+0000", + "1976-11-18T152330.1+0000", + "19761118T15:23:30.1+0000", + "19761118T152330.1+00:00", + "19761118T152330.1+0000", + "+001976-11-18T152330.1+00:00", + "+0019761118T15:23:30.1+00:00", + "+001976-11-18T15:23:30.1+0000", + "+001976-11-18T152330.1+0000", + "+0019761118T15:23:30.1+0000", + "+0019761118T152330.1+00:00", + "+0019761118T152330.1+0000", + "1976-11-18T15:23", + "1976-11-18T15", + "1976-11-18", + ]; + }, + + /* + * PlainYearMonth-like strings that are valid and should produce November of + * the ISO year -9999. + */ + plainYearMonthStringsValidNegativeYear() { + return [ + "\u2212009999-11", + ]; + }, + } +}; diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/subtract.js b/js/src/tests/test262/staging/Temporal/Duration/old/subtract.js new file mode 100644 index 0000000000..619dd54e9b --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Duration/old/subtract.js @@ -0,0 +1,105 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-duration-objects +description: Temporal.Duration.prototype.subtract() works as expected +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var oneDay = new Temporal.Duration(0, 0, 0, 1); +var hours24 = new Temporal.Duration(0, 0, 0, 0, 24); + +// relativeTo does not affect days if PlainDate +var relativeTo = Temporal.PlainDate.from("2017-01-01"); +assert.sameValue(`${ oneDay.subtract(hours24, { relativeTo }) }`, "PT0S"); + +// relativeTo does not affect days if ZonedDateTime, and duration encompasses no DST change +var relativeTo = Temporal.ZonedDateTime.from("2017-01-01T00:00[+04:30]"); +assert.sameValue(`${ oneDay.subtract(hours24, { relativeTo }) }`, "PT0S"); + +// relativeTo affects days if ZonedDateTime, and duration encompasses DST change +var timeZone = TemporalHelpers.springForwardFallBackTimeZone(); +var skippedHourDay = Temporal.PlainDateTime.from("2000-04-02").toZonedDateTime(timeZone); +var repeatedHourDay = Temporal.PlainDateTime.from("2000-10-29").toZonedDateTime(timeZone); +var inRepeatedHour = new Temporal.ZonedDateTime(972806400_000_000_000n, timeZone); +var twoDays = new Temporal.Duration(0, 0, 0, 2); +var threeDays = new Temporal.Duration(0, 0, 0, 3); + +// start inside repeated hour, end after +assert.sameValue(`${ hours24.subtract(oneDay, { relativeTo: inRepeatedHour }) }`, "-PT1H"); +assert.sameValue(`${ oneDay.subtract(hours24, { relativeTo: inRepeatedHour }) }`, "PT1H"); + +// start in normal hour, end in skipped hour +var relativeTo = Temporal.PlainDateTime.from("2000-04-01T02:30").toZonedDateTime(timeZone); +assert.sameValue(`${ hours24.subtract(oneDay, { relativeTo }) }`, "PT1H"); +assert.sameValue(`${ oneDay.subtract(hours24, { relativeTo }) }`, "PT0S"); + +// start before skipped hour, end >1 day after +assert.sameValue(`${ threeDays.subtract(hours24, { relativeTo: skippedHourDay }) }`, "P2D"); +assert.sameValue(`${ hours24.subtract(threeDays, { relativeTo: skippedHourDay }) }`, "-P1DT23H"); + +// start before skipped hour, end <1 day after +assert.sameValue(`${ twoDays.subtract(hours24, { relativeTo: skippedHourDay }) }`, "P1D"); +assert.sameValue(`${ hours24.subtract(twoDays, { relativeTo: skippedHourDay }) }`, "-PT23H"); + +// start before repeated hour, end >1 day after +assert.sameValue(`${ threeDays.subtract(hours24, { relativeTo: repeatedHourDay }) }`, "P2D"); +assert.sameValue(`${ hours24.subtract(threeDays, { relativeTo: repeatedHourDay }) }`, "-P2DT1H"); + +// start before repeated hour, end <1 day after +assert.sameValue(`${ twoDays.subtract(hours24, { relativeTo: repeatedHourDay }) }`, "P1D"); +assert.sameValue(`${ hours24.subtract(twoDays, { relativeTo: repeatedHourDay }) }`, "-P1DT1H"); + +// Samoa skipped 24 hours +var fakeSamoa = TemporalHelpers.crossDateLineTimeZone(); +var relativeTo = Temporal.PlainDateTime.from("2011-12-29T12:00").toZonedDateTime(fakeSamoa); +assert.sameValue(`${ twoDays.subtract(Temporal.Duration.from({ hours: 48 }), { relativeTo }) }`, "-P1D"); +assert.sameValue(`${ Temporal.Duration.from({ hours: 48 }).subtract(twoDays, { relativeTo }) }`, "P2D"); + +// casts relativeTo to ZonedDateTime if possible +assert.sameValue(`${ oneDay.subtract(hours24, { + relativeTo: { + year: 2000, + month: 10, + day: 29, + timeZone + } +}) }`, "PT1H"); + +// casts relativeTo to PlainDate if possible +assert.sameValue(`${ oneDay.subtract(hours24, { relativeTo: "2019-11-02" }) }`, "PT0S"); +assert.sameValue(`${ oneDay.subtract(hours24, { + relativeTo: { + year: 2019, + month: 11, + day: 2 + } +}) }`, "PT0S"); + +// throws on wrong offset for ZonedDateTime relativeTo string +assert.throws(RangeError, () => oneDay.subtract(hours24, { relativeTo: "1971-01-01T00:00+02:00[-00:44:30]" })); + +// at least the required properties must be present in relativeTo +assert.throws(TypeError, () => oneDay.subtract(hours24, { + relativeTo: { + month: 11, + day: 3 + } +})); +assert.throws(TypeError, () => oneDay.subtract(hours24, { + relativeTo: { + year: 2019, + month: 11 + } +})); +assert.throws(TypeError, () => oneDay.subtract(hours24, { + relativeTo: { + year: 2019, + day: 3 + } +})); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/toString.js b/js/src/tests/test262/staging/Temporal/Duration/old/toString.js new file mode 100644 index 0000000000..1f7e91fb06 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Duration/old/toString.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-duration-objects +description: toString() works as expected +features: [Temporal] +---*/ + +// serializing balance doesn't lose precision when values are precise +var d = Temporal.Duration.from({ + milliseconds: Number.MAX_SAFE_INTEGER, + microseconds: Number.MAX_SAFE_INTEGER +}); +assert.sameValue(`${ d }`, "PT9016206453995.731991S"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/total.js b/js/src/tests/test262/staging/Temporal/Duration/old/total.js new file mode 100644 index 0000000000..75f4d526db --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Duration/old/total.js @@ -0,0 +1,453 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-duration-objects +description: Temporal.Duration.prototype.total() +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var d = new Temporal.Duration(5, 5, 5, 5, 5, 5, 5, 5, 5, 5); +var d2 = new Temporal.Duration(0, 0, 0, 5, 5, 5, 5, 5, 5, 5); +var relativeTo = Temporal.PlainDate.from("2020-01-01"); + +// throws on disallowed or invalid unit (object param) +[ + "era", + "nonsense" +].forEach(unit => { + assert.throws(RangeError, () => d.total({ unit })); +}); + +// throws on disallowed or invalid unit (string param) +[ + "era", + "nonsense" +].forEach(unit => { + assert.throws(RangeError, () => d.total(unit)); +}); + +// does not lose precision for seconds and smaller units +var s = Temporal.Duration.from({ + milliseconds: 2, + microseconds: 31 +}).total({ unit: "seconds" }); +assert.sameValue(s, 0.002031); + +// accepts datetime strings or fields for relativeTo +[ + "2020-01-01", + "20200101", + "2020-01-01T00:00:00.000000000", + { + year: 2020, + month: 1, + day: 1 + } +].forEach(relativeTo => { + var daysPastJuly1 = 5 * 7 + 5 - 30; + var partialDayNanos = d.hours * 3600000000000 + d.minutes * 60000000000 + d.seconds * 1000000000 + d.milliseconds * 1000000 + d.microseconds * 1000 + d.nanoseconds; + var partialDay = partialDayNanos / (3600000000000 * 24); + var partialMonth = (daysPastJuly1 + partialDay) / 31; + var totalMonths = 5 * 12 + 5 + 1 + partialMonth; + var total = d.total({ + unit: "months", + relativeTo + }); + assert.sameValue(total.toPrecision(15), totalMonths.toPrecision(15)); +}); + +// does not accept non-string primitives for relativeTo +[ + 20200101, + 20200101n, + null, + true, +].forEach(relativeTo => { + assert.throws( + TypeError, () => d.total({ unit: "months", relativeTo}) + ); +}); + +// throws on wrong offset for ZonedDateTime relativeTo string +assert.throws(RangeError, () => d.total({ + unit: "months", + relativeTo: "1971-01-01T00:00+02:00[-00:44:30]" +})); + +// relativeTo object must contain at least the required correctly-spelled properties +assert.throws(TypeError, () => d.total({ + unit: "months", + relativeTo: {} +})); +assert.throws(TypeError, () => d.total({ + unit: "months", + relativeTo: { + years: 2020, + month: 1, + day: 1 + } +})); + +// incorrectly-spelled properties are ignored in relativeTo +var oneMonth = Temporal.Duration.from({ months: 1 }); +assert.sameValue(oneMonth.total({ + unit: "months", + relativeTo: { + year: 2020, + month: 1, + day: 1, + months: 2 + } +}), 1); + +// throws RangeError if unit property is missing +[ + {}, + () => { + }, + { roundingMode: "ceil" } +].forEach(roundTo => assert.throws(RangeError, () => d.total(roundTo))); + +// relativeTo required to round calendar units even in durations w/o calendar units (object param) +assert.throws(RangeError, () => d2.total({ unit: "years" })); +assert.throws(RangeError, () => d2.total({ unit: "months" })); +assert.throws(RangeError, () => d2.total({ unit: "weeks" })); + +// relativeTo required to round calendar units even in durations w/o calendar units (string param) +assert.throws(RangeError, () => d2.total("years")); +assert.throws(RangeError, () => d2.total("months")); +assert.throws(RangeError, () => d2.total("weeks")); + +// relativeTo is required to round durations with calendar units (object param) +assert.throws(RangeError, () => d.total({ unit: "years" })); +assert.throws(RangeError, () => d.total({ unit: "months" })); +assert.throws(RangeError, () => d.total({ unit: "weeks" })); +assert.throws(RangeError, () => d.total({ unit: "days" })); +assert.throws(RangeError, () => d.total({ unit: "hours" })); +assert.throws(RangeError, () => d.total({ unit: "minutes" })); +assert.throws(RangeError, () => d.total({ unit: "seconds" })); +assert.throws(RangeError, () => d.total({ unit: "milliseconds" })); +assert.throws(RangeError, () => d.total({ unit: "microseconds" })); +assert.throws(RangeError, () => d.total({ unit: "nanoseconds" })); + +// relativeTo is required to round durations with calendar units (string param) +assert.throws(RangeError, () => d.total("years")); +assert.throws(RangeError, () => d.total("months")); +assert.throws(RangeError, () => d.total("weeks")); +assert.throws(RangeError, () => d.total("days")); +assert.throws(RangeError, () => d.total("hours")); +assert.throws(RangeError, () => d.total("minutes")); +assert.throws(RangeError, () => d.total("seconds")); +assert.throws(RangeError, () => d.total("milliseconds")); +assert.throws(RangeError, () => d.total("microseconds")); +assert.throws(RangeError, () => d.total("nanoseconds")); +var d2Nanoseconds = d2.days * 24 * 3600000000000 + d2.hours * 3600000000000 + d2.minutes * 60000000000 + d2.seconds * 1000000000 + d2.milliseconds * 1000000 + d2.microseconds * 1000 + d2.nanoseconds; +var totalD2 = { + days: d2Nanoseconds / (24 * 3600000000000), + hours: d2Nanoseconds / 3600000000000, + minutes: d2Nanoseconds / 60000000000, + seconds: d2Nanoseconds / 1000000000, + milliseconds: d2Nanoseconds / 1000000, + microseconds: d2Nanoseconds / 1000, + nanoseconds: d2Nanoseconds +}; + +// relativeTo not required to round fixed-length units in durations without variable units +assert(Math.abs(d2.total({ unit: "days" }) - totalD2.days) < Number.EPSILON); +assert(Math.abs(d2.total({ unit: "hours" }) - totalD2.hours) < Number.EPSILON); +assert(Math.abs(d2.total({ unit: "minutes" }) - totalD2.minutes) < Number.EPSILON); +assert(Math.abs(d2.total({ unit: "seconds" }) - totalD2.seconds) < Number.EPSILON); +assert(Math.abs(d2.total({ unit: "milliseconds" }) - totalD2.milliseconds) < Number.EPSILON); +assert(Math.abs(d2.total({ unit: "microseconds" }) - totalD2.microseconds) < Number.EPSILON); +assert.sameValue(d2.total({ unit: "nanoseconds" }), totalD2.nanoseconds); + +// relativeTo not required to round fixed-length units in durations without variable units (negative) +var negativeD2 = d2.negated(); +assert(Math.abs(negativeD2.total({ unit: "days" }) - -totalD2.days) < Number.EPSILON); +assert(Math.abs(negativeD2.total({ unit: "hours" }) - -totalD2.hours) < Number.EPSILON); +assert(Math.abs(negativeD2.total({ unit: "minutes" }) - -totalD2.minutes) < Number.EPSILON); +assert(Math.abs(negativeD2.total({ unit: "seconds" }) - -totalD2.seconds) < Number.EPSILON); +assert(Math.abs(negativeD2.total({ unit: "milliseconds" }) - -totalD2.milliseconds) < Number.EPSILON); +assert(Math.abs(negativeD2.total({ unit: "microseconds" }) - -totalD2.microseconds) < Number.EPSILON); +assert.sameValue(negativeD2.total({ unit: "nanoseconds" }), -totalD2.nanoseconds); +var endpoint = relativeTo.toPlainDateTime().add(d); +var options = unit => ({ + largestUnit: unit, + smallestUnit: unit, + roundingMode: "trunc" +}); +var fullYears = 5; +var fullDays = endpoint.since(relativeTo, options("days")).days; +var fullMilliseconds = endpoint.since(relativeTo, options("milliseconds")).milliseconds; +var partialDayMilliseconds = fullMilliseconds - fullDays * 24 * 3600000 + 0.005005; +var fractionalDay = partialDayMilliseconds / (24 * 3600000); +var partialYearDays = fullDays - (fullYears * 365 + 2); +var fractionalYear = partialYearDays / 365 + fractionalDay / 365; +var fractionalMonths = ((endpoint.day - 1) * (24 * 3600000) + partialDayMilliseconds) / (31 * 24 * 3600000); +var totalResults = { + years: fullYears + fractionalYear, + months: 66 + fractionalMonths, + weeks: (fullDays + fractionalDay) / 7, + days: fullDays + fractionalDay, + hours: fullDays * 24 + partialDayMilliseconds / 3600000, + minutes: fullDays * 24 * 60 + partialDayMilliseconds / 60000, + seconds: fullDays * 24 * 60 * 60 + partialDayMilliseconds / 1000, + milliseconds: fullMilliseconds + 0.005005, + microseconds: fullMilliseconds * 1000 + 5.005, + nanoseconds: fullMilliseconds * 1000000 + 5005 +}; +for (var [unit, expected] of Object.entries(totalResults)) { + assert.sameValue(d.total({ + unit, + relativeTo + }).toPrecision(15), expected.toPrecision(15)); +} +for (var unit of [ + "microseconds", + "nanoseconds" + ]) { + assert(d.total({ + unit, + relativeTo + }).toString().startsWith("174373505005")); +} + +// balances differently depending on relativeTo +var fortyDays = Temporal.Duration.from({ days: 40 }); +assert.sameValue(fortyDays.total({ + unit: "months", + relativeTo: "2020-02-01" +}).toPrecision(16), (1 + 11 / 31).toPrecision(16)); +assert.sameValue(fortyDays.total({ + unit: "months", + relativeTo: "2020-01-01" +}).toPrecision(16), (1 + 9 / 29).toPrecision(16)); + +// balances differently depending on relativeTo (negative) +var negativeFortyDays = Temporal.Duration.from({ days: -40 }); +assert.sameValue(negativeFortyDays.total({ + unit: "months", + relativeTo: "2020-03-01" +}).toPrecision(16), (-(1 + 11 / 31)).toPrecision(16)); +assert.sameValue(negativeFortyDays.total({ + unit: "months", + relativeTo: "2020-04-01" +}).toPrecision(16), (-(1 + 9 / 29)).toPrecision(16)); + +var oneDay = new Temporal.Duration(0, 0, 0, 1); +// relativeTo does not affect days if PlainDate +var relativeTo = Temporal.PlainDate.from("2017-01-01"); +assert.sameValue(oneDay.total({ + unit: "hours", + relativeTo +}), 24); + +// relativeTo does not affect days if ZonedDateTime, and duration encompasses no DST change +var relativeTo = Temporal.ZonedDateTime.from("2017-01-01T00:00[+04:30]"); +assert.sameValue(oneDay.total({ + unit: "hours", + relativeTo +}), 24); + +// relativeTo affects days if ZonedDateTime, and duration encompasses DST change" +var timeZone = TemporalHelpers.springForwardFallBackTimeZone(); +var skippedHourDay = Temporal.PlainDateTime.from("2000-04-02").toZonedDateTime(timeZone); +var repeatedHourDay = Temporal.PlainDateTime.from("2000-10-29").toZonedDateTime(timeZone); +var inRepeatedHour = new Temporal.ZonedDateTime(972806400_000_000_000n, timeZone); +var hours12 = new Temporal.Duration(0, 0, 0, 0, 12); +var hours25 = new Temporal.Duration(0, 0, 0, 0, 25); + +// start inside repeated hour, end after +assert.sameValue(hours25.total({ + unit: "days", + relativeTo: inRepeatedHour +}), 1); +assert.sameValue(oneDay.total({ + unit: "hours", + relativeTo: inRepeatedHour +}), 25); + +// start after repeated hour, end inside (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-10-30T01:00").toZonedDateTime(timeZone); +assert.sameValue(hours25.negated().total({ + unit: "days", + relativeTo +}), -1); +assert.sameValue(oneDay.negated().total({ + unit: "hours", + relativeTo +}), -25); + +// start in normal hour, end in skipped hour +var relativeTo = Temporal.PlainDateTime.from("2000-04-01T02:30").toZonedDateTime(timeZone); +var totalDays = hours25.total({ + unit: "days", + relativeTo +}); +assert(Math.abs(totalDays - (1 + 1 / 24)) < Number.EPSILON); +assert.sameValue(oneDay.total({ + unit: "hours", + relativeTo +}), 24); + +// start before skipped hour, end >1 day after +var totalDays = hours25.total({ + unit: "days", + relativeTo: skippedHourDay +}); +assert(Math.abs(totalDays - (1 + 2 / 24)) < Number.EPSILON); +assert.sameValue(oneDay.total({ + unit: "hours", + relativeTo: skippedHourDay +}), 23); + +// start after skipped hour, end >1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-04-03T00:00").toZonedDateTime(timeZone); +var totalDays = hours25.negated().total({ + unit: "days", + relativeTo +}); +assert(Math.abs(totalDays - (-1 - 2 / 24)) < Number.EPSILON); +assert.sameValue(oneDay.negated().total({ + unit: "hours", + relativeTo +}), -23); + +// start before skipped hour, end <1 day after +var totalDays = hours12.total({ + unit: "days", + relativeTo: skippedHourDay +}); +assert(Math.abs(totalDays - 12 / 23) < Number.EPSILON); + +// start after skipped hour, end <1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-04-02T12:00").toZonedDateTime(timeZone); +var totalDays = hours12.negated().total({ + unit: "days", + relativeTo +}); +assert(Math.abs(totalDays - -12 / 23) < Number.EPSILON); + +// start before repeated hour, end >1 day after +assert.sameValue(hours25.total({ + unit: "days", + relativeTo: repeatedHourDay +}), 1); +assert.sameValue(oneDay.total({ + unit: "hours", + relativeTo: repeatedHourDay +}), 25); + +// start after repeated hour, end >1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-10-30T00:00").toZonedDateTime(timeZone); +assert.sameValue(hours25.negated().total({ + unit: "days", + relativeTo +}), -1); +assert.sameValue(oneDay.negated().total({ + unit: "hours", + relativeTo +}), -25); + +// start before repeated hour, end <1 day after +var totalDays = hours12.total({ + unit: "days", + relativeTo: repeatedHourDay +}); +assert(Math.abs(totalDays - 12 / 25) < Number.EPSILON); + +// start after repeated hour, end <1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-10-29T12:00").toZonedDateTime(timeZone); +var totalDays = hours12.negated().total({ + unit: "days", + relativeTo +}); +assert(Math.abs(totalDays - -12 / 25) < Number.EPSILON); + +// Samoa skipped 24 hours +var fakeSamoa = TemporalHelpers.crossDateLineTimeZone(); +var relativeTo = Temporal.PlainDateTime.from("2011-12-29T12:00").toZonedDateTime(fakeSamoa); +var totalDays = hours25.total({ + unit: "days", + relativeTo +}); +assert(Math.abs(totalDays - (2 + 1 / 24)) < Number.EPSILON); +assert.sameValue(Temporal.Duration.from({ hours: 48 }).total({ + unit: "days", + relativeTo +}), 3); +assert.sameValue(Temporal.Duration.from({ days: 2 }).total({ + unit: "hours", + relativeTo +}), 24); +assert.sameValue(Temporal.Duration.from({ days: 3 }).total({ + unit: "hours", + relativeTo +}), 48); + +// totaling back up to days +var relativeTo = Temporal.PlainDateTime.from("2000-10-28T00:00").toZonedDateTime(timeZone); +assert.sameValue(Temporal.Duration.from({ hours: 48 }).total({ unit: "days" }), 2); +var totalDays = Temporal.Duration.from({ hours: 48 }).total({ + unit: "days", + relativeTo +}); +assert(Math.abs(totalDays - (1 + 24 / 25)) < Number.EPSILON); + +// casts relativeTo to ZonedDateTime if possible +assert.sameValue(oneDay.total({ + unit: "hours", + relativeTo: { + year: 2000, + month: 10, + day: 29, + timeZone + } +}), 25); + +// balances up to the next unit after rounding +var almostWeek = Temporal.Duration.from({ + days: 6, + hours: 20 +}); +var totalWeeks = almostWeek.total({ + unit: "weeks", + relativeTo: "2020-01-01" +}); +assert(Math.abs(totalWeeks - (6 + 20 / 24) / 7) < Number.EPSILON); + +// balances up to the next unit after rounding (negative) +var almostWeek = Temporal.Duration.from({ + days: -6, + hours: -20 +}); +var totalWeeks = almostWeek.total({ + unit: "weeks", + relativeTo: "2020-01-01" +}); +assert(Math.abs(totalWeeks - -((6 + 20 / 24) / 7)) < Number.EPSILON); + +// balances days up to both years and months +var twoYears = Temporal.Duration.from({ + months: 11, + days: 396 +}); +assert.sameValue(twoYears.total({ + unit: "years", + relativeTo: "2017-01-01" +}), 2); + +// balances days up to both years and months (negative) +var twoYears = Temporal.Duration.from({ + months: -11, + days: -396 +}); +assert.sameValue(twoYears.total({ + unit: "years", + relativeTo: "2017-01-01" +}), -2); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Duration/shell.js b/js/src/tests/test262/staging/Temporal/Duration/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Duration/shell.js diff --git a/js/src/tests/test262/staging/Temporal/Instant/browser.js b/js/src/tests/test262/staging/Temporal/Instant/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/browser.js diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/add.js b/js/src/tests/test262/staging/Temporal/Instant/old/add.js new file mode 100644 index 0000000000..b2e2b05831 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/old/add.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-instant-objects +description: Temporal.Instant.add works +features: [Temporal] +---*/ + +var inst = Temporal.Instant.from("1969-12-25T12:23:45.678901234Z"); + +// cross epoch in ms +var one = inst.subtract({ + hours: 240, + nanoseconds: 800 +}); +var two = inst.add({ + hours: 240, + nanoseconds: 800 +}); +var three = two.subtract({ + hours: 480, + nanoseconds: 1600 +}); +var four = one.add({ + hours: 480, + nanoseconds: 1600 +}); +assert.sameValue(`${ one }`, "1969-12-15T12:23:45.678900434Z", `(${ inst }).subtract({ hours: 240, nanoseconds: 800 }) = ${ one }`); +assert.sameValue(`${ two }`, "1970-01-04T12:23:45.678902034Z", `(${ inst }).add({ hours: 240, nanoseconds: 800 }) = ${ two }`); +assert(three.equals(one), `(${ two }).subtract({ hours: 480, nanoseconds: 1600 }) = ${ one }`); +assert(four.equals(two), `(${ one }).add({ hours: 480, nanoseconds: 1600 }) = ${ two }`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/browser.js b/js/src/tests/test262/staging/Temporal/Instant/old/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/old/browser.js diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/compare.js b/js/src/tests/test262/staging/Temporal/Instant/old/compare.js new file mode 100644 index 0000000000..aab0b9bcfc --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/old/compare.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-instant-objects +description: Temporal.Instant.compare works +features: [Temporal] +---*/ + +var i1 = Temporal.Instant.from("1963-02-13T09:36:29.123456789Z"); +var i2 = Temporal.Instant.from("1976-11-18T15:23:30.123456789Z"); +var i3 = Temporal.Instant.from("1981-12-15T14:34:31.987654321Z"); + +// pre epoch equal +assert.sameValue(Temporal.Instant.compare(i1, Temporal.Instant.from(i1)), 0) + +// epoch equal +assert.sameValue(Temporal.Instant.compare(i2, Temporal.Instant.from(i2)), 0) + +// cross epoch smaller/larger +assert.sameValue(Temporal.Instant.compare(i1, i2), -1) + +// cross epoch larger/smaller +assert.sameValue(Temporal.Instant.compare(i2, i1), 1) + +// epoch smaller/larger +assert.sameValue(Temporal.Instant.compare(i2, i3), -1) + +// epoch larger/smaller +assert.sameValue(Temporal.Instant.compare(i3, i2), 1) + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/equals.js b/js/src/tests/test262/staging/Temporal/Instant/old/equals.js new file mode 100644 index 0000000000..2df0d8db50 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/old/equals.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-instant-objects +description: Temporal.Instant.equals works +features: [Temporal] +---*/ + +var i1 = Temporal.Instant.from("1963-02-13T09:36:29.123456789Z"); +var i2 = Temporal.Instant.from("1976-11-18T15:23:30.123456789Z"); +var i3 = Temporal.Instant.from("1981-12-15T14:34:31.987654321Z"); + +// pre epoch equal +assert(i1.equals(i1)) + +// epoch equal +assert(i2.equals(i2)) + +// cross epoch unequal +assert(!i1.equals(i2)) + +// epoch unequal +assert(!i2.equals(i3)) + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/limits.js b/js/src/tests/test262/staging/Temporal/Instant/old/limits.js new file mode 100644 index 0000000000..adeeaaf508 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/old/limits.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-instant-objects +description: Min/max range +features: [Temporal] +---*/ + + +// constructing from ns +var limit = 8640000000000000000000n; +assert.throws(RangeError, () => new Temporal.Instant(-limit - 1n)); +assert.throws(RangeError, () => new Temporal.Instant(limit + 1n)); +assert.sameValue(`${ new Temporal.Instant(-limit) }`, "-271821-04-20T00:00:00Z"); +assert.sameValue(`${ new Temporal.Instant(limit) }`, "+275760-09-13T00:00:00Z"); + +// constructing from ms +var limit = 8640000000000000; +assert.throws(RangeError, () => Temporal.Instant.fromEpochMilliseconds(-limit - 1)); +assert.throws(RangeError, () => Temporal.Instant.fromEpochMilliseconds(limit + 1)); +assert.sameValue(`${ Temporal.Instant.fromEpochMilliseconds(-limit) }`, "-271821-04-20T00:00:00Z"); +assert.sameValue(`${ Temporal.Instant.fromEpochMilliseconds(limit) }`, "+275760-09-13T00:00:00Z"); + +// converting from DateTime +var min = Temporal.PlainDateTime.from("-271821-04-19T00:00:00.000000001"); +var max = Temporal.PlainDateTime.from("+275760-09-13T23:59:59.999999999"); +var utc = Temporal.TimeZone.from("UTC"); +assert.throws(RangeError, () => utc.getInstantFor(min)); +assert.throws(RangeError, () => utc.getInstantFor(max)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/round.js b/js/src/tests/test262/staging/Temporal/Instant/old/round.js new file mode 100644 index 0000000000..6a5d41c878 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/old/round.js @@ -0,0 +1,131 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-instant-objects +description: Temporal.Instant.round works +features: [Temporal] +---*/ + +var inst = Temporal.Instant.from("1976-11-18T14:23:30.123456789Z"); + +// throws without required smallestUnit parameter +assert.throws(RangeError, () => inst.round({})); +assert.throws(RangeError, () => inst.round({ + roundingIncrement: 1, + roundingMode: "ceil" +})); + +// rounds to an increment of hours +assert.sameValue(`${ inst.round({ + smallestUnit: "hour", + roundingIncrement: 4 +}) }`, "1976-11-18T16:00:00Z"); + +// rounds to an increment of minutes +assert.sameValue(`${ inst.round({ + smallestUnit: "minute", + roundingIncrement: 15 +}) }`, "1976-11-18T14:30:00Z"); + +// rounds to an increment of seconds +assert.sameValue(`${ inst.round({ + smallestUnit: "second", + roundingIncrement: 30 +}) }`, "1976-11-18T14:23:30Z"); + +// rounds to an increment of milliseconds +assert.sameValue(`${ inst.round({ + smallestUnit: "millisecond", + roundingIncrement: 10 +}) }`, "1976-11-18T14:23:30.12Z"); + +// rounds to an increment of microseconds +assert.sameValue(`${ inst.round({ + smallestUnit: "microsecond", + roundingIncrement: 10 +}) }`, "1976-11-18T14:23:30.12346Z"); + +// rounds to an increment of nanoseconds +assert.sameValue(`${ inst.round({ + smallestUnit: "nanosecond", + roundingIncrement: 10 +}) }`, "1976-11-18T14:23:30.12345679Z"); + +// rounds to days by specifying increment of 86400 seconds in various units +var expected = "1976-11-19T00:00:00Z"; +assert.sameValue(`${ inst.round({ + smallestUnit: "hour", + roundingIncrement: 24 +}) }`, expected); +assert.sameValue(`${ inst.round({ + smallestUnit: "minute", + roundingIncrement: 1440 +}) }`, expected); +assert.sameValue(`${ inst.round({ + smallestUnit: "second", + roundingIncrement: 86400 +}) }`, expected); +assert.sameValue(`${ inst.round({ + smallestUnit: "millisecond", + roundingIncrement: 86400000 +}) }`, expected); + +// allows increments that divide evenly into solar days +assert(inst.round({ + smallestUnit: "second", + roundingIncrement: 864 +}) instanceof Temporal.Instant); + +// throws on increments that do not divide evenly into solar days +assert.throws(RangeError, () => inst.round({ + smallestUnit: "hour", + roundingIncrement: 7 +})); +assert.throws(RangeError, () => inst.round({ + smallestUnit: "minute", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => inst.round({ + smallestUnit: "second", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => inst.round({ + smallestUnit: "millisecond", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => inst.round({ + smallestUnit: "microsecond", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => inst.round({ + smallestUnit: "nanosecond", + roundingIncrement: 29 +})); + +// accepts plural units +[ + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond" +].forEach(smallestUnit => { + assert(inst.round({ smallestUnit }).equals(inst.round({ smallestUnit: `${ smallestUnit }s` }))); +}); + +// accepts string parameter as shortcut for {smallestUnit} +[ + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond" +].forEach(smallestUnit => { + assert(inst.round(smallestUnit).equals(inst.round({ smallestUnit }))); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/shell.js b/js/src/tests/test262/staging/Temporal/Instant/old/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/old/shell.js diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/since.js b/js/src/tests/test262/staging/Temporal/Instant/old/since.js new file mode 100644 index 0000000000..6dbc8bc0e1 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/old/since.js @@ -0,0 +1,249 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-instant-objects +description: Temporal.Instant.since() works +features: [Temporal] +---*/ + +var earlier = Temporal.Instant.from("1976-11-18T15:23:30.123456789Z"); +var later = Temporal.Instant.from("2019-10-29T10:46:38.271986102Z"); +var diff = later.since(earlier); +assert.sameValue(`${ earlier.since(later) }`, `${ diff.negated() }`) +assert.sameValue(`${ earlier.until(later) }`, `${ diff }`) +assert(earlier.add(diff).equals(later)) +assert(later.subtract(diff).equals(earlier)) +var feb20 = Temporal.Instant.from("2020-02-01T00:00Z"); +var feb21 = Temporal.Instant.from("2021-02-01T00:00Z"); + +// can return minutes and hours +assert.sameValue(`${ feb21.since(feb20, { largestUnit: "hours" }) }`, "PT8784H"); +assert.sameValue(`${ feb21.since(feb20, { largestUnit: "minutes" }) }`, "PT527040M"); + +// can return subseconds +var latersub = feb20.add({ + hours: 24, + milliseconds: 250, + microseconds: 250, + nanoseconds: 250 +}); +var msDiff = latersub.since(feb20, { largestUnit: "milliseconds" }); +assert.sameValue(msDiff.seconds, 0); +assert.sameValue(msDiff.milliseconds, 86400250); +assert.sameValue(msDiff.microseconds, 250); +assert.sameValue(msDiff.nanoseconds, 250); +var µsDiff = latersub.since(feb20, { largestUnit: "microseconds" }); +assert.sameValue(µsDiff.milliseconds, 0); +assert.sameValue(µsDiff.microseconds, 86400250250); +assert.sameValue(µsDiff.nanoseconds, 250); +var nsDiff = latersub.since(feb20, { largestUnit: "nanoseconds" }); +assert.sameValue(nsDiff.microseconds, 0); +assert.sameValue(nsDiff.nanoseconds, 86400250250250); + +// options may be a function object +assert.sameValue(`${ feb21.since(feb20, () => { +}) }`, "PT31622400S"); + +// assumes a different default for largestUnit if smallestUnit is larger than seconds +assert.sameValue(`${ later.since(earlier, { + smallestUnit: "hours", + roundingMode: "halfExpand" +}) }`, "PT376435H"); +assert.sameValue(`${ later.since(earlier, { + smallestUnit: "minutes", + roundingMode: "halfExpand" +}) }`, "PT22586123M"); +var largestUnit = "hours"; + +// rounds to an increment of hours +assert.sameValue(`${ later.since(earlier, { + largestUnit, + smallestUnit: "hours", + roundingIncrement: 3, + roundingMode: "halfExpand" +}) }`, "PT376434H"); + +// rounds to an increment of minutes +assert.sameValue(`${ later.since(earlier, { + largestUnit, + smallestUnit: "minutes", + roundingIncrement: 30, + roundingMode: "halfExpand" +}) }`, "PT376435H30M"); + +// rounds to an increment of seconds +assert.sameValue(`${ later.since(earlier, { + largestUnit, + smallestUnit: "seconds", + roundingIncrement: 15, + roundingMode: "halfExpand" +}) }`, "PT376435H23M15S"); + +// rounds to an increment of milliseconds +assert.sameValue(`${ later.since(earlier, { + largestUnit, + smallestUnit: "milliseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT376435H23M8.15S"); + +// rounds to an increment of microseconds +assert.sameValue(`${ later.since(earlier, { + largestUnit, + smallestUnit: "microseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT376435H23M8.14853S"); + +// rounds to an increment of nanoseconds +assert.sameValue(`${ later.since(earlier, { + largestUnit, + smallestUnit: "nanoseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT376435H23M8.14852931S"); + +// valid hour increments divide into 24 +[ + 1, + 2, + 3, + 4, + 6, + 8, + 12 +].forEach(roundingIncrement => { + var options = { + largestUnit, + smallestUnit: "hours", + roundingIncrement + }; + assert(later.since(earlier, options) instanceof Temporal.Duration); +}); + +// valid increments divide into 60 +[ + "minutes", + "seconds" +].forEach(smallestUnit => { + [ + 1, + 2, + 3, + 4, + 5, + 6, + 10, + 12, + 15, + 20, + 30 + ].forEach(roundingIncrement => { + var options = { + largestUnit, + smallestUnit, + roundingIncrement + }; + assert(later.since(earlier, options) instanceof Temporal.Duration); + }); +}); + +// valid increments divide into 1000 +[ + "milliseconds", + "microseconds", + "nanoseconds" +].forEach(smallestUnit => { + [ + 1, + 2, + 4, + 5, + 8, + 10, + 20, + 25, + 40, + 50, + 100, + 125, + 200, + 250, + 500 + ].forEach(roundingIncrement => { + var options = { + largestUnit, + smallestUnit, + roundingIncrement + }; + assert(later.since(earlier, options) instanceof Temporal.Duration); + }); +}); + +// throws on increments that do not divide evenly into the next highest +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "hours", + roundingIncrement: 11 +})); +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "minutes", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "seconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "milliseconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "microseconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "nanoseconds", + roundingIncrement: 29 +})); + +// throws on increments that are equal to the next highest +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "hours", + roundingIncrement: 24 +})); +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "minutes", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "seconds", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "milliseconds", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "microseconds", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "nanoseconds", + roundingIncrement: 1000 +})); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/toZonedDateTime.js b/js/src/tests/test262/staging/Temporal/Instant/old/toZonedDateTime.js new file mode 100644 index 0000000000..85a7432434 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/old/toZonedDateTime.js @@ -0,0 +1,61 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-instant-objects +description: Temporal.Instant.toZonedDateTime() works +features: [Temporal] +---*/ + +var inst = Temporal.Instant.from("1976-11-18T14:23:30.123456789Z"); + +// throws without parameter +assert.throws(TypeError, () => inst.toZonedDateTime()); + +// throws with a string parameter +assert.throws(TypeError, () => inst.toZonedDateTime("UTC")); + +var fakeGregorian = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "gregory", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; + +// time zone parameter UTC +var timeZone = Temporal.TimeZone.from("UTC"); +var zdt = inst.toZonedDateTime({ + timeZone, + calendar: fakeGregorian, +}); +assert.sameValue(inst.epochNanoseconds, zdt.epochNanoseconds); +assert.sameValue(`${ zdt }`, "1976-11-18T14:23:30.123456789+00:00[UTC][u-ca=gregory]"); + +// time zone parameter non-UTC +var timeZone = Temporal.TimeZone.from("-05:00"); +var zdt = inst.toZonedDateTime({ + timeZone, + calendar: fakeGregorian, +}); +assert.sameValue(inst.epochNanoseconds, zdt.epochNanoseconds); +assert.sameValue(`${ zdt }`, "1976-11-18T09:23:30.123456789-05:00[-05:00][u-ca=gregory]"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/toZonedDateTimeISO.js b/js/src/tests/test262/staging/Temporal/Instant/old/toZonedDateTimeISO.js new file mode 100644 index 0000000000..2fbaad8b9d --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/old/toZonedDateTimeISO.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-instant-objects +description: Temporal.Instant.toZonedDateTimeISO() works +features: [Temporal] +---*/ + +var inst = Temporal.Instant.from("1976-11-18T14:23:30.123456789Z"); + +// throws without parameter +assert.throws(TypeError, () => inst.toZonedDateTimeISO()); + +// time zone parameter UTC +var tz = Temporal.TimeZone.from("UTC"); +var zdt = inst.toZonedDateTimeISO(tz); +assert.sameValue(inst.epochNanoseconds, zdt.epochNanoseconds); +assert.sameValue(`${ zdt }`, "1976-11-18T14:23:30.123456789+00:00[UTC]"); + +// time zone parameter non-UTC +var tz = Temporal.TimeZone.from("-05:00"); +var zdt = inst.toZonedDateTimeISO(tz); +assert.sameValue(inst.epochNanoseconds, zdt.epochNanoseconds); +assert.sameValue(`${ zdt }`, "1976-11-18T09:23:30.123456789-05:00[-05:00]"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/until.js b/js/src/tests/test262/staging/Temporal/Instant/old/until.js new file mode 100644 index 0000000000..78f3c11189 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/old/until.js @@ -0,0 +1,249 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-instant-objects +description: Temporal.Instant.until() works +features: [Temporal] +---*/ + +var earlier = Temporal.Instant.from("1969-07-24T16:50:35.123456789Z"); +var later = Temporal.Instant.from("2019-10-29T10:46:38.271986102Z"); +var diff = earlier.until(later); +assert.sameValue(`${ later.until(earlier) }`, `${ diff.negated() }`) +assert.sameValue(`${ later.since(earlier) }`, `${ diff }`) +assert(earlier.add(diff).equals(later)) +assert(later.subtract(diff).equals(earlier)) +var feb20 = Temporal.Instant.from("2020-02-01T00:00Z"); +var feb21 = Temporal.Instant.from("2021-02-01T00:00Z"); + +// can return minutes and hours +assert.sameValue(`${ feb20.until(feb21, { largestUnit: "hours" }) }`, "PT8784H"); +assert.sameValue(`${ feb20.until(feb21, { largestUnit: "minutes" }) }`, "PT527040M"); + +// can return subseconds +var latersub = feb20.add({ + hours: 24, + milliseconds: 250, + microseconds: 250, + nanoseconds: 250 +}); +var msDiff = feb20.until(latersub, { largestUnit: "milliseconds" }); +assert.sameValue(msDiff.seconds, 0); +assert.sameValue(msDiff.milliseconds, 86400250); +assert.sameValue(msDiff.microseconds, 250); +assert.sameValue(msDiff.nanoseconds, 250); +var µsDiff = feb20.until(latersub, { largestUnit: "microseconds" }); +assert.sameValue(µsDiff.milliseconds, 0); +assert.sameValue(µsDiff.microseconds, 86400250250); +assert.sameValue(µsDiff.nanoseconds, 250); +var nsDiff = feb20.until(latersub, { largestUnit: "nanoseconds" }); +assert.sameValue(nsDiff.microseconds, 0); +assert.sameValue(nsDiff.nanoseconds, 86400250250250); + +// options may be a function object +assert.sameValue(`${ feb20.until(feb21, () => { +}) }`, "PT31622400S"); + +// assumes a different default for largestUnit if smallestUnit is larger than seconds +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "hours", + roundingMode: "halfExpand" +}) }`, "PT440610H"); +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "minutes", + roundingMode: "halfExpand" +}) }`, "PT26436596M"); +var largestUnit = "hours"; + +// rounds to an increment of hours +assert.sameValue(`${ earlier.until(later, { + largestUnit, + smallestUnit: "hours", + roundingIncrement: 4, + roundingMode: "halfExpand" +}) }`, "PT440608H"); + +// rounds to an increment of minutes +assert.sameValue(`${ earlier.until(later, { + largestUnit, + smallestUnit: "minutes", + roundingIncrement: 30, + roundingMode: "halfExpand" +}) }`, "PT440610H"); + +// rounds to an increment of seconds +assert.sameValue(`${ earlier.until(later, { + largestUnit, + smallestUnit: "seconds", + roundingIncrement: 15, + roundingMode: "halfExpand" +}) }`, "PT440609H56M"); + +// rounds to an increment of milliseconds +assert.sameValue(`${ earlier.until(later, { + largestUnit, + smallestUnit: "milliseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT440609H56M3.15S"); + +// rounds to an increment of microseconds +assert.sameValue(`${ earlier.until(later, { + largestUnit, + smallestUnit: "microseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT440609H56M3.14853S"); + +// rounds to an increment of nanoseconds +assert.sameValue(`${ earlier.until(later, { + largestUnit, + smallestUnit: "nanoseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT440609H56M3.14852931S"); + +// valid hour increments divide into 24 +[ + 1, + 2, + 3, + 4, + 6, + 8, + 12 +].forEach(roundingIncrement => { + var options = { + largestUnit, + smallestUnit: "hours", + roundingIncrement + }; + assert(earlier.until(later, options) instanceof Temporal.Duration); +}); + +// valid increments divide into 60 +[ + "minutes", + "seconds" +].forEach(smallestUnit => { + [ + 1, + 2, + 3, + 4, + 5, + 6, + 10, + 12, + 15, + 20, + 30 + ].forEach(roundingIncrement => { + var options = { + largestUnit, + smallestUnit, + roundingIncrement + }; + assert(earlier.until(later, options) instanceof Temporal.Duration); + }); +}); + +// valid increments divide into 1000 +[ + "milliseconds", + "microseconds", + "nanoseconds" +].forEach(smallestUnit => { + [ + 1, + 2, + 4, + 5, + 8, + 10, + 20, + 25, + 40, + 50, + 100, + 125, + 200, + 250, + 500 + ].forEach(roundingIncrement => { + var options = { + largestUnit, + smallestUnit, + roundingIncrement + }; + assert(earlier.until(later, options) instanceof Temporal.Duration); + }); +}); + +// throws on increments that do not divide evenly into the next highest +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "hours", + roundingIncrement: 11 +})); +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "minutes", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "seconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "milliseconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "microseconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "nanoseconds", + roundingIncrement: 29 +})); + +// throws on increments that are equal to the next highest +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "hours", + roundingIncrement: 24 +})); +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "minutes", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "seconds", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "milliseconds", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "microseconds", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "nanoseconds", + roundingIncrement: 1000 +})); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Instant/shell.js b/js/src/tests/test262/staging/Temporal/Instant/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/shell.js diff --git a/js/src/tests/test262/staging/Temporal/Regex/browser.js b/js/src/tests/test262/staging/Temporal/Regex/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/browser.js diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/browser.js b/js/src/tests/test262/staging/Temporal/Regex/old/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/old/browser.js diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/duration.js b/js/src/tests/test262/staging/Temporal/Regex/old/duration.js new file mode 100644 index 0000000000..8ea46a4202 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/old/duration.js @@ -0,0 +1,271 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-duration-objects +description: Temporal.Duration works as expected +features: [Temporal] +---*/ + +function test(isoString, components) { + var {y = 0, mon = 0, w = 0, d = 0, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0} = components; + var duration = Temporal.Duration.from(isoString); + assert.sameValue(duration.years, y); + assert.sameValue(duration.months, mon); + assert.sameValue(duration.weeks, w); + assert.sameValue(duration.days, d); + assert.sameValue(duration.hours, h); + assert.sameValue(duration.minutes, min); + assert.sameValue(duration.seconds, s); + assert.sameValue(duration.milliseconds, ms); + assert.sameValue(duration.microseconds, µs); + assert.sameValue(duration.nanoseconds, ns); +} +var day = [ + [ + "", + {} + ], + [ + "1Y", + { y: 1 } + ], + [ + "2M", + { mon: 2 } + ], + [ + "4W", + { w: 4 } + ], + [ + "3D", + { d: 3 } + ], + [ + "1Y2M", + { + y: 1, + mon: 2 + } + ], + [ + "1Y3D", + { + y: 1, + d: 3 + } + ], + [ + "2M3D", + { + mon: 2, + d: 3 + } + ], + [ + "4W3D", + { + w: 4, + d: 3 + } + ], + [ + "1Y2M3D", + { + y: 1, + mon: 2, + d: 3 + } + ], + [ + "1Y2M4W3D", + { + y: 1, + mon: 2, + w: 4, + d: 3 + } + ] +]; +var times = [ + [ + "", + {} + ], + [ + "4H", + { h: 4 } + ], + [ + "5M", + { min: 5 } + ], + [ + "4H5M", + { + h: 4, + min: 5 + } + ] +]; +var sec = [ + [ + "", + {} + ], + [ + "6S", + { s: 6 } + ], + [ + "7.1S", + { + s: 7, + ms: 100 + } + ], + [ + "7.12S", + { + s: 7, + ms: 120 + } + ], + [ + "7.123S", + { + s: 7, + ms: 123 + } + ], + [ + "8.1234S", + { + s: 8, + ms: 123, + µs: 400 + } + ], + [ + "8.12345S", + { + s: 8, + ms: 123, + µs: 450 + } + ], + [ + "8.123456S", + { + s: 8, + ms: 123, + µs: 456 + } + ], + [ + "9.1234567S", + { + s: 9, + ms: 123, + µs: 456, + ns: 700 + } + ], + [ + "9.12345678S", + { + s: 9, + ms: 123, + µs: 456, + ns: 780 + } + ], + [ + "9.123456789S", + { + s: 9, + ms: 123, + µs: 456, + ns: 789 + } + ], + [ + "0.123S", + { ms: 123 } + ], + [ + "0,123S", + { ms: 123 } + ], + [ + "0.123456S", + { + ms: 123, + µs: 456 + } + ], + [ + "0,123456S", + { + ms: 123, + µs: 456 + } + ], + [ + "0.123456789S", + { + ms: 123, + µs: 456, + ns: 789 + } + ], + [ + "0,123456789S", + { + ms: 123, + µs: 456, + ns: 789 + } + ] +]; +var tim = sec.reduce((arr, [s, add]) => arr.concat(times.map(([p, expect]) => [ + `${ p }${ s }`, + { + ...expect, + ...add + } +])), []).slice(1); +day.slice(1).forEach(([p, expect]) => { + test(`P${ p }`, expect); + test(`p${ p }`, expect); + test(`p${ p.toLowerCase() }`, expect); +}); +tim.forEach(([p, expect]) => { + test(`PT${ p }`, expect); + test(`Pt${ p }`, expect); + test(`pt${ p.toLowerCase() }`, expect); +}); +for (var [d, dexpect] of day) { + for (var [t, texpect] of tim) { + test(`P${ d }T${ t }`, { + ...dexpect, + ...texpect + }); + test(`p${ d }T${ t.toLowerCase() }`, { + ...dexpect, + ...texpect + }); + test(`P${ d.toLowerCase() }t${ t }`, { + ...dexpect, + ...texpect + }); + test(`p${ d.toLowerCase() }t${ t.toLowerCase() }`, { + ...dexpect, + ...texpect + }); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/instant.js b/js/src/tests/test262/staging/Temporal/Regex/old/instant.js new file mode 100644 index 0000000000..39b03d4d1b --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/old/instant.js @@ -0,0 +1,255 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-instant-objects +description: Temporal.Instant works as expected +features: [Temporal] +---*/ + +function test(isoString, components) { + var [y, mon, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = components; + var instant = Temporal.Instant.from(isoString); + var utc = Temporal.TimeZone.from("UTC"); + var datetime = utc.getPlainDateTimeFor(instant); + assert.sameValue(datetime.year, y); + assert.sameValue(datetime.month, mon); + assert.sameValue(datetime.day, d); + assert.sameValue(datetime.hour, h); + assert.sameValue(datetime.minute, min); + assert.sameValue(datetime.second, s); + assert.sameValue(datetime.millisecond, ms); + assert.sameValue(datetime.microsecond, µs); + assert.sameValue(datetime.nanosecond, ns); +} +function generateTest(dateTimeString, zoneString, components) { + test(`${ dateTimeString }${ zoneString }`, components.slice(0, 5)); + test(`${ dateTimeString }:30${ zoneString }`, components.slice(0, 6)); + test(`${ dateTimeString }:30.123456789${ zoneString }`, components); +} +// valid strings +[ + "+01:00", + "+01", + "+0100", +].forEach(zoneString => { + generateTest("1976-11-18T15:23", `${ zoneString }[Europe/Vienna]`, [ + 1976, + 11, + 18, + 14, + 23, + 30, + 123, + 456, + 789 + ]); + generateTest("1976-11-18T15:23", `+01:00[${ zoneString }]`, [ + 1976, + 11, + 18, + 14, + 23, + 30, + 123, + 456, + 789 + ]); +}); +[ + "-04:00", + "-04", + "-0400", + "-04:00:00", + "-040000", + "-04:00:00.000000000", + "-040000.0" +].forEach(zoneString => generateTest("1976-11-18T15:23", zoneString, [ + 1976, + 11, + 18, + 19, + 23, + 30, + 123, + 456, + 789 +])); +test("1976-11-18T15:23:30.1Z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 100 +]); +test("1976-11-18T15:23:30.12Z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 120 +]); +test("1976-11-18T15:23:30.123Z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123 +]); +test("1976-11-18T15:23:30.1234Z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123, + 400 +]); +test("1976-11-18T15:23:30.12345Z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123, + 450 +]); +test("1976-11-18T15:23:30.123456Z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123, + 456 +]); +test("1976-11-18T15:23:30.1234567Z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123, + 456, + 700 +]); +test("1976-11-18T15:23:30.12345678Z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123, + 456, + 780 +]); +generateTest("1976-11-18T15:23", "z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123, + 456, + 789 +]); +test("1976-11-18T15:23:30,1234Z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123, + 400 +]); +[ + "\u221204:00", + "\u221204", + "\u22120400" +].forEach(offset => test(`1976-11-18T15:23:30.1234${ offset }`, [ + 1976, + 11, + 18, + 19, + 23, + 30, + 123, + 400 +])); +test("\u2212009999-11-18T15:23:30.1234Z", [ + -9999, + 11, + 18, + 15, + 23, + 30, + 123, + 400 +]); +test("1976-11-18T152330Z", [ + 1976, + 11, + 18, + 15, + 23, + 30 +]); +test("1976-11-18T152330.1234Z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123, + 400 +]); +test("19761118T15:23:30Z", [ + 1976, + 11, + 18, + 15, + 23, + 30 +]); +test("19761118T152330Z", [ + 1976, + 11, + 18, + 15, + 23, + 30 +]); +test("19761118T152330.1234Z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123, + 400 +]); +test("1976-11-18T15Z", [ + 1976, + 11, + 18, + 15 +]); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/plaindate.js b/js/src/tests/test262/staging/Temporal/Regex/old/plaindate.js new file mode 100644 index 0000000000..d757efe5be --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/old/plaindate.js @@ -0,0 +1,84 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-plainedate-objects +description: Temporal.PlainDate works as expected +features: [Temporal] +---*/ + +function test(isoString, components) { + var [y, m, d, cid = "iso8601"] = components; + var date = Temporal.PlainDate.from(isoString); + assert.sameValue(date.year, y); + assert.sameValue(date.month, m); + assert.sameValue(date.day, d); + assert.sameValue(date.calendarId, cid); +} +function generateTest(dateTimeString, zoneString) { + var components = [ + 1976, + 11, + 18 + ]; + test(`${ dateTimeString }${ zoneString }`, components); + test(`${ dateTimeString }:30${ zoneString }`, components); + test(`${ dateTimeString }:30.123456789${ zoneString }`, components); +} +[ + "+0100[Europe/Vienna]", + "[Europe/Vienna]", + "+01:00[Custom/Vienna]", + "-0400", + "-04:00:00.000000000", + "+01:00[+01:00]", + "+01:00[+0100]", + "" +].forEach(zoneString => generateTest("1976-11-18T15:23", zoneString)); +[ + "1", + "12", + "123", + "1234", + "12345", + "123456", + "1234567", + "12345678" +].forEach(decimals => test(`1976-11-18T15:23:30.${ decimals }`, [ + 1976, + 11, + 18 +])); +test("1976-11-18T15:23:30,1234", [ + 1976, + 11, + 18 +]); +test("\u2212009999-11-18", [ + -9999, + 11, + 18 +]); +test("1976-11-18T15", [ + 1976, + 11, + 18 +]); +[ + "", + "+01:00[Europe/Vienna]", + "[Europe/Vienna]", + "+01:00[Custom/Vienna]" +].forEach(zoneString => test(`1976-11-18T15:23:30.123456789${ zoneString }[u-ca=iso8601]`, [ + 1976, + 11, + 18 +])); +test("1976-11-18[u-ca=iso8601]", [ + 1976, + 11, + 18 +]); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/plaindatetime.js b/js/src/tests/test262/staging/Temporal/Regex/old/plaindatetime.js new file mode 100644 index 0000000000..2e08edf249 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/old/plaindatetime.js @@ -0,0 +1,74 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-plaindatetime-objects +description: Temporal.PlainDateTime works as expected +features: [Temporal] +---*/ + +function test(isoString, components) { + var [y, mon, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0, cid = "iso8601"] = components; + var datetime = Temporal.PlainDateTime.from(isoString); + assert.sameValue(datetime.year, y); + assert.sameValue(datetime.month, mon); + assert.sameValue(datetime.day, d); + assert.sameValue(datetime.hour, h); + assert.sameValue(datetime.minute, min); + assert.sameValue(datetime.second, s); + assert.sameValue(datetime.millisecond, ms); + assert.sameValue(datetime.microsecond, µs); + assert.sameValue(datetime.nanosecond, ns); + assert.sameValue(datetime.calendarId, cid); +} +function generateTest(dateTimeString, zoneString) { + var components = [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123, + 456, + 789 + ]; + test(`${ dateTimeString }${ zoneString }`, components.slice(0, 5)); + test(`${ dateTimeString }:30${ zoneString }`, components.slice(0, 6)); + test(`${ dateTimeString }:30.123456789${ zoneString }`, components); +} + +//valid strings +[ + "+0100[Europe/Vienna]", + "+01:00[Europe/Vienna]", + "[Europe/Vienna]", + "+01:00[Custom/Vienna]", + "-0400", + "-04:00", + "-04:00:00.000000000", + "+010000.0[Europe/Vienna]", + "+01:00[+01:00]", + "+01:00[+0100]", + "" +].forEach(zoneString => generateTest("1976-11-18T15:23", zoneString)); +[ + "", + "+01:00[Europe/Vienna]", + "+01:00[Custom/Vienna]", + "[Europe/Vienna]" +].forEach(zoneString => test(`1976-11-18T15:23:30.123456789${ zoneString }[u-ca=iso8601]`, [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123, + 456, + 789 +])); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/plainmonthday.js b/js/src/tests/test262/staging/Temporal/Regex/old/plainmonthday.js new file mode 100644 index 0000000000..c6969e45d1 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/old/plainmonthday.js @@ -0,0 +1,78 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-plainmonthday-objects +description: Temporal.PlainMonthDay works as expected +features: [Temporal] +---*/ + +function test(isoString, components) { + var [m, d, cid = "iso8601"] = components; + var monthDay = Temporal.PlainMonthDay.from(isoString); + assert.sameValue(monthDay.monthCode, `M${ m.toString().padStart(2, "0") }`); + assert.sameValue(monthDay.day, d); + assert.sameValue(monthDay.calendarId, cid); +} +function generateTest(dateTimeString, zoneString) { + var components = [ + 11, + 18 + ]; + test(`${ dateTimeString }${ zoneString }`, components); + test(`${ dateTimeString }:30${ zoneString }`, components); + test(`${ dateTimeString }:30.123456789${ zoneString }`, components); +} +[ + "+0100[Europe/Vienna]", + "[Europe/Vienna]", + "+01:00[Custom/Vienna]", + "-0400", + "-04:00:00.000000000", + "+010000.0[Europe/Vienna]", + "+01:00[+01:00]", + "+01:00[+0100]", + "" +].forEach(zoneString => generateTest("1976-11-18T15:23", zoneString)); +[ + "1", + "12", + "123", + "1234", + "12345", + "123456", + "1234567", + "12345678" +].forEach(decimals => test(`1976-11-18T15:23:30.${ decimals }`, [ + 11, + 18 +])); +[ + "1976-11-18T15:23:30,1234", + "\u2212009999-11-18", + "19761118", + "+199999-11-18", + "+1999991118", + "-000300-11-18", + "-0003001118", + "15121118" +].forEach(str => test(str, [ + 11, + 18 +])); +[ + "", + "+01:00[Europe/Vienna]", + "[Europe/Vienna]", + "+01:00[Custom/Vienna]" +].forEach(zoneString => test(`1976-11-18T15:23:30.123456789${ zoneString }[u-ca=iso8601]`, [ + 11, + 18 +])); +test("1972-11-18[u-ca=iso8601]", [ + 11, + 18 +]); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/plaintime.js b/js/src/tests/test262/staging/Temporal/Regex/old/plaintime.js new file mode 100644 index 0000000000..def35d7af2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/old/plaintime.js @@ -0,0 +1,65 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-plaintime-objects +description: Temporal.PlainTime works as expected +features: [Temporal] +---*/ + +function test(isoString, components) { + var [h = 0, m = 0, s = 0, ms = 0, µs = 0, ns = 0] = components; + var time = Temporal.PlainTime.from(isoString); + assert.sameValue(time.hour, h); + assert.sameValue(time.minute, m); + assert.sameValue(time.second, s); + assert.sameValue(time.millisecond, ms); + assert.sameValue(time.microsecond, µs); + assert.sameValue(time.nanosecond, ns); +} +function generateTest(dateTimeString, zoneString) { + var components = [ + 15, + 23, + 30, + 123, + 456, + 789 + ]; + test(`${ dateTimeString }${ zoneString }`, components.slice(0, 2)); + test(`${ dateTimeString }:30${ zoneString }`, components.slice(0, 3)); + test(`${ dateTimeString }:30.123456789${ zoneString }`, components); +} +[ + "+0100[Europe/Vienna]", + "+01:00[Custom/Vienna]", + "-0400", + "-04:00:00.000000000", + "+010000.0[Europe/Vienna]", + "+01:00[+01:00]", + "+01:00[+0100]", + "" +].forEach(zoneString => generateTest("1976-11-18T15:23", zoneString)); +[ + "+01:00[Europe/Vienna]", + "[Europe/Vienna]", + "+01:00[Custom/Vienna]", + "-04:00", + "" +].forEach(zoneStr => test(`15${ zoneStr }`, [15])); +[ + "", + "+01:00[Europe/Vienna]", + "[Europe/Vienna]", + "+01:00[Custom/Vienna]" +].forEach(zoneString => test(`1976-11-18T15:23:30.123456789${ zoneString }[u-ca=iso8601]`, [ + 15, + 23, + 30, + 123, + 456, + 789 +])); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/plainyearmonth.js b/js/src/tests/test262/staging/Temporal/Regex/old/plainyearmonth.js new file mode 100644 index 0000000000..54fbd85e93 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/old/plainyearmonth.js @@ -0,0 +1,109 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-plainyearmonth-objects +description: Temporal.PlainYearMonth works as expected +features: [Temporal] +---*/ + +function test(isoString, components) { + var [y, m, cid = "iso8601"] = components; + var yearMonth = Temporal.PlainYearMonth.from(isoString); + assert.sameValue(yearMonth.year, y); + assert.sameValue(yearMonth.month, m); + assert.sameValue(yearMonth.calendarId, cid); +} +function generateTest(dateTimeString, zoneString) { + var components = [ + 1976, + 11 + ]; + test(`${ dateTimeString }${ zoneString }`, components); + test(`${ dateTimeString }:30${ zoneString }`, components); + test(`${ dateTimeString }:30.123456789${ zoneString }`, components); +} +[ + "+0100[Europe/Vienna]", + "[Europe/Vienna]", + "+01:00[Custom/Vienna]", + "-0400", + "-04:00:00.000000000", + "+01:00:00.0[Europe/Vienna]", + "+01:00[+01:00]", + "+01:00[+0100]", + "" +].forEach(zoneString => generateTest("1976-11-18T15:23", zoneString)); +[ + "1", + "12", + "123", + "1234", + "12345", + "123456", + "1234567", + "12345678" +].forEach(decimals => test(`1976-11-18T15:23:30.${ decimals }`, [ + 1976, + 11 +])); +test("1976-11-18T15:23:30,1234", [ + 1976, + 11 +]); +test("19761118", [ + 1976, + 11 +]); +test("+199999-11-18", [ + 199999, + 11 +]); +test("+1999991118", [ + 199999, + 11 +]); +test("-000300-11-18", [ + -300, + 11 +]); +test("-0003001118", [ + -300, + 11 +]); +test("1512-11-18", [ + 1512, + 11 +]); +test("+199999-11", [ + 199999, + 11 +]); +test("+19999911", [ + 199999, + 11 +]); +test("-000300-11", [ + -300, + 11 +]); +test("-00030011", [ + -300, + 11 +]); +[ + "", + "+01:00[Europe/Vienna]", + "[Europe/Vienna]", + "+01:00[Custom/Vienna]" +].forEach(zoneString => test(`1976-11-18T15:23:30.123456789${ zoneString }[u-ca=iso8601]`, [ + 1976, + 11 +])); +test("1976-11-01[u-ca=iso8601]", [ + 1976, + 11 +]); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/shell.js b/js/src/tests/test262/staging/Temporal/Regex/old/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/old/shell.js diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/timezone.js b/js/src/tests/test262/staging/Temporal/Regex/old/timezone.js new file mode 100644 index 0000000000..60ffd043bf --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/old/timezone.js @@ -0,0 +1,91 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-timezone-objects +description: Temporal.TimeZone works as expected +features: [Temporal] +---*/ + +function test(offsetString, expectedName) { + var timeZone = Temporal.TimeZone.from(offsetString); + assert.sameValue(timeZone.id, expectedName); +} +function generateTest(dateTimeString, zoneString, expectedName) { + test(`${ dateTimeString }${ zoneString }`, expectedName); + test(`${ dateTimeString }:30${ zoneString }`, expectedName); + test(`${ dateTimeString }:30.123456789${ zoneString }`, expectedName); +} +[ + "+00:00", + "+00", + "+0000" +].forEach(zoneString => { + generateTest("1976-11-18T15:23", `${ zoneString }[UTC]`, "UTC"); + generateTest("1976-11-18T15:23", `+00:00[${ zoneString }]`, "+00:00"); +}); +[ + "-04:00", + "-04", + "-0400" +].forEach(zoneString => generateTest("1976-11-18T15:23", zoneString, "-04:00")); +[ + "1", + "12", + "123", + "1234", + "12345", + "123456", + "1234567", + "12345678" +].forEach(decimals => { + test(`1976-11-18T15:23:30.${ decimals }Z`, "UTC"); +}); +generateTest("1976-11-18T15:23", "z", "UTC"); +test("1976-11-18T15:23:30,1234Z", "UTC"); +test("1976-11-18T15:23+000000,0[UTC]", "UTC"); +[ + "\u221204:00", + "\u221204", + "\u22120400" +].forEach(offset => test(`1976-11-18T15:23${ offset }`, "-04:00")); +[ + "1976-11-18T152330", + "1976-11-18T152330.1234", + "19761118T15:23:30", + "19761118T152330", + "19761118T152330.1234", + "1976-11-18T15" +].forEach(dateTimeString => { + [ + "+00:00", + "+00", + "+0000", + "" + ].forEach(zoneString => test(`${ dateTimeString }${ zoneString }[UTC]`, "UTC")); + [ + "-04:00", + "-04", + "-0400" + ].forEach(zoneString => test(`${ dateTimeString }${ zoneString }`, "-04:00")); + test(`${ dateTimeString }Z`, "UTC"); +}); +test("-0000", "+00:00"); +test("-00:00", "+00:00"); +test("+00", "+00:00"); +test("-00", "+00:00"); +test("+03", "+03:00"); +test("-03", "-03:00"); +test("\u22120000", "+00:00"); +test("\u221200:00", "+00:00"); +test("\u221200", "+00:00"); +test("\u22120300", "-03:00"); +test("\u221203:00", "-03:00"); +test("\u221203", "-03:00"); +test("1976-11-18T15:23:30.123456789Z[u-ca=iso8601]", "UTC"); +test("1976-11-18T15:23:30.123456789-04:00[u-ca=iso8601]", "-04:00"); +test("1976-11-18T15:23:30.123456789[UTC][u-ca=iso8601]", "UTC"); +test("1976-11-18T15:23:30.123456789+00:00[UTC][u-ca=iso8601]", "UTC"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Regex/shell.js b/js/src/tests/test262/staging/Temporal/Regex/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/shell.js diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/browser.js b/js/src/tests/test262/staging/Temporal/TimeZone/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/browser.js diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/browser.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/browser.js diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/dst-change.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/dst-change.js new file mode 100644 index 0000000000..8db7aa7e03 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/dst-change.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-timezone-objects +description: with DST change +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// clock moving forward +var zone = TemporalHelpers.springForwardFallBackTimeZone(); +var dtm = new Temporal.PlainDateTime(2000, 4, 2, 2, 45); +assert.sameValue(`${ zone.getInstantFor(dtm) }`, "2000-04-02T10:45:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "earlier" }) }`, "2000-04-02T09:45:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "later" }) }`, "2000-04-02T10:45:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "compatible" }) }`, "2000-04-02T10:45:00Z"); +assert.throws(RangeError, () => zone.getInstantFor(dtm, { disambiguation: "reject" })); + +// clock moving backward +var dtm = new Temporal.PlainDateTime(2000, 10, 29, 1, 45); +assert.sameValue(`${ zone.getInstantFor(dtm) }`, "2000-10-29T08:45:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "earlier" }) }`, "2000-10-29T08:45:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "later" }) }`, "2000-10-29T09:45:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "compatible" }) }`, "2000-10-29T08:45:00Z"); +assert.throws(RangeError, () => zone.getInstantFor(dtm, { disambiguation: "reject" })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/getInstantFor-disambiguation.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/getInstantFor-disambiguation.js new file mode 100644 index 0000000000..101a7d32a1 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/getInstantFor-disambiguation.js @@ -0,0 +1,50 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-timezone-objects +description: getInstantFor disambiguation +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var dtm = new Temporal.PlainDateTime(2000, 10, 29, 1, 45); + +// with constant offset +var zone = Temporal.TimeZone.from("+03:30"); +for (var disambiguation of [ + undefined, + "compatible", + "earlier", + "later", + "reject" + ]) { + assert(zone.getInstantFor(dtm, { disambiguation }) instanceof Temporal.Instant); +} + +// with daylight saving change - Fall +var zone = TemporalHelpers.springForwardFallBackTimeZone(); +assert.sameValue(`${ zone.getInstantFor(dtm) }`, "2000-10-29T08:45:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "earlier" }) }`, "2000-10-29T08:45:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "later" }) }`, "2000-10-29T09:45:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "compatible" }) }`, "2000-10-29T08:45:00Z"); +assert.throws(RangeError, () => zone.getInstantFor(dtm, { disambiguation: "reject" })); + +// with daylight saving change - Spring +var dtmLA = new Temporal.PlainDateTime(2000, 4, 2, 2, 30); +assert.sameValue(`${ zone.getInstantFor(dtmLA) }`, "2000-04-02T10:30:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtmLA, { disambiguation: "earlier" }) }`, "2000-04-02T09:30:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtmLA, { disambiguation: "later" }) }`, "2000-04-02T10:30:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtmLA, { disambiguation: "compatible" }) }`, "2000-04-02T10:30:00Z"); +assert.throws(RangeError, () => zone.getInstantFor(dtmLA, { disambiguation: "reject" })); + +// throws on bad disambiguation +var zone = Temporal.TimeZone.from("+03:30"); +[ + "", + "EARLIER", + "test", +].forEach(disambiguation => assert.throws(RangeError, () => zone.getInstantFor(dtm, { disambiguation }))); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/getInstantFor.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/getInstantFor.js new file mode 100644 index 0000000000..37c2be715f --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/getInstantFor.js @@ -0,0 +1,61 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-timezone-objects +description: Temporal.TimeZone.prototype.getInstantFor() works +features: [Temporal] +---*/ + + +// recent date +var dt = Temporal.PlainDateTime.from("2019-10-29T10:46:38.271986102"); +var tz = Temporal.TimeZone.from("+01:00"); +assert.sameValue(`${ tz.getInstantFor(dt) }`, "2019-10-29T09:46:38.271986102Z"); + +// year ≤ 99 +var dt = Temporal.PlainDateTime.from("0098-10-29T10:46:38.271986102"); +var tz = Temporal.TimeZone.from("+06:00"); +assert.sameValue(`${ tz.getInstantFor(dt) }`, "0098-10-29T04:46:38.271986102Z"); +dt = Temporal.PlainDateTime.from("+000098-10-29T10:46:38.271986102"); +assert.sameValue(`${ tz.getInstantFor(dt) }`, "0098-10-29T04:46:38.271986102Z"); + +// year < 1 +var dt = Temporal.PlainDateTime.from("0000-10-29T10:46:38.271986102"); +var tz = Temporal.TimeZone.from("+06:00"); +assert.sameValue(`${ tz.getInstantFor(dt) }`, "0000-10-29T04:46:38.271986102Z"); +dt = Temporal.PlainDateTime.from("+000000-10-29T10:46:38.271986102"); +assert.sameValue(`${ tz.getInstantFor(dt) }`, "0000-10-29T04:46:38.271986102Z"); +dt = Temporal.PlainDateTime.from("-001000-10-29T10:46:38.271986102"); +assert.sameValue(`${ tz.getInstantFor(dt) }`, "-001000-10-29T04:46:38.271986102Z"); + +// year 0 leap day +var dt = Temporal.PlainDateTime.from("0000-02-29T00:00"); +var tz = Temporal.TimeZone.from("-00:01"); +assert.sameValue(`${ tz.getInstantFor(dt) }`, "0000-02-29T00:01:00Z"); +dt = Temporal.PlainDateTime.from("+000000-02-29T00:00"); +assert.sameValue(`${ tz.getInstantFor(dt) }`, "0000-02-29T00:01:00Z"); + +// outside of Instant range +var max = Temporal.PlainDateTime.from("+275760-09-13T23:59:59.999999999"); +var offsetTz = Temporal.TimeZone.from("-01:00"); +assert.throws(RangeError, () => offsetTz.getInstantFor(max)); +var namedTz = Temporal.TimeZone.from("Etc/GMT+12"); +assert.throws(RangeError, () => namedTz.getInstantFor(max)); + +// casts argument +var tz = Temporal.TimeZone.from("+01:00"); +assert.sameValue(`${ tz.getInstantFor("2019-10-29T10:46:38.271986102") }`, "2019-10-29T09:46:38.271986102Z"); +assert.sameValue(`${ tz.getInstantFor({ + year: 2019, + month: 10, + day: 29, + hour: 10, + minute: 46, + second: 38 +}) }`, "2019-10-29T09:46:38Z"); + + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/getNextTransition.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/getNextTransition.js new file mode 100644 index 0000000000..50e043ff83 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/getNextTransition.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-timezone-objects +description: Temporal.TimeZone.prototype.getNextTransition() works as expected +features: [Temporal] +---*/ + +var noTransitionTZ = Temporal.TimeZone.from("Etc/GMT+10"); + +// should work for timezones with no scheduled transitions in the near future +var start = Temporal.Instant.from("1945-10-15T13:00:00Z"); +assert.sameValue(noTransitionTZ.getNextTransition(start), null); + +// accepts string as argument +assert.sameValue(noTransitionTZ.getNextTransition("2019-04-16T21:01Z"), null); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/getPossibleInstantsFor.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/getPossibleInstantsFor.js new file mode 100644 index 0000000000..6eda88fdb0 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/getPossibleInstantsFor.js @@ -0,0 +1,46 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-timezone-objects +description: Temporal.TimeZone.prototype.getPossibleInstantsFor() works as expected +includes: [deepEqual.js, temporalHelpers.js] +features: [Temporal] +---*/ + + +// with constant offset +var zone = Temporal.TimeZone.from("+03:30"); +var dt = Temporal.PlainDateTime.from("2019-02-16T23:45"); +assert.deepEqual(zone.getPossibleInstantsFor(dt).map(a => `${ a }`), ["2019-02-16T20:15:00Z"]); + +// with clock moving forward +var zone = TemporalHelpers.springForwardFallBackTimeZone(); +var dt = Temporal.PlainDateTime.from("2000-04-02T02:45"); +assert.deepEqual(zone.getPossibleInstantsFor(dt), []); + +// with clock moving backward +var dt = Temporal.PlainDateTime.from("2000-10-29T01:45"); +assert.deepEqual(zone.getPossibleInstantsFor(dt).map(a => `${ a }`), [ + "2000-10-29T08:45:00Z", + "2000-10-29T09:45:00Z" +]); + +// casts argument +var tz = Temporal.TimeZone.from("+03:30"); +assert.deepEqual(tz.getPossibleInstantsFor({ + year: 2019, + month: 2, + day: 16, + hour: 23, + minute: 45, + second: 30 +}).map(a => `${ a }`), ["2019-02-16T20:15:30Z"]); +assert.deepEqual(tz.getPossibleInstantsFor("2019-02-16T23:45:30").map(a => `${ a }`), ["2019-02-16T20:15:30Z"]); + +// object must contain at least the required properties +var tz = Temporal.TimeZone.from("UTC"); +assert.throws(TypeError, () => tz.getPossibleInstantsFor({ year: 2019 })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/getPreviousTransition.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/getPreviousTransition.js new file mode 100644 index 0000000000..6c6b703b58 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/getPreviousTransition.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-timezone-objects +description: Temporal.TimeZone.prototype.getPreviousTransition() works +features: [Temporal] +---*/ + +var utc = Temporal.TimeZone.from("UTC"); + +// no transitions without a TZDB +var instant = Temporal.Instant.from("2020-06-11T21:01Z"); +assert.sameValue(utc.getPreviousTransition(instant), null); + +// accepts string as argument +assert.sameValue(utc.getPreviousTransition("2020-06-11T21:01Z"), null); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/shell.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/shell.js new file mode 100644 index 0000000000..eaa8761415 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/shell.js @@ -0,0 +1,2487 @@ +// GENERATED, DO NOT EDIT +// file: deepEqual.js +// Copyright 2019 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: > + Compare two values structurally +defines: [assert.deepEqual] +---*/ + +assert.deepEqual = function(actual, expected, message) { + var format = assert.deepEqual.format; + assert( + assert.deepEqual._compare(actual, expected), + `Expected ${format(actual)} to be structurally equal to ${format(expected)}. ${(message || '')}` + ); +}; + +assert.deepEqual.format = function(value, seen) { + switch (typeof value) { + case 'string': + return typeof JSON !== "undefined" ? JSON.stringify(value) : `"${value}"`; + case 'number': + case 'boolean': + case 'symbol': + case 'bigint': + return value.toString(); + case 'undefined': + return 'undefined'; + case 'function': + return `[Function${value.name ? `: ${value.name}` : ''}]`; + case 'object': + if (value === null) return 'null'; + if (value instanceof Date) return `Date "${value.toISOString()}"`; + if (value instanceof RegExp) return value.toString(); + if (!seen) { + seen = { + counter: 0, + map: new Map() + }; + } + + let usage = seen.map.get(value); + if (usage) { + usage.used = true; + return `[Ref: #${usage.id}]`; + } + + usage = { id: ++seen.counter, used: false }; + seen.map.set(value, usage); + + if (typeof Set !== "undefined" && value instanceof Set) { + return `Set {${Array.from(value).map(value => assert.deepEqual.format(value, seen)).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`; + } + if (typeof Map !== "undefined" && value instanceof Map) { + return `Map {${Array.from(value).map(pair => `${assert.deepEqual.format(pair[0], seen)} => ${assert.deepEqual.format(pair[1], seen)}}`).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`; + } + if (Array.isArray ? Array.isArray(value) : value instanceof Array) { + return `[${value.map(value => assert.deepEqual.format(value, seen)).join(', ')}]${usage.used ? ` as #${usage.id}` : ''}`; + } + let tag = Symbol.toStringTag in value ? value[Symbol.toStringTag] : 'Object'; + if (tag === 'Object' && Object.getPrototypeOf(value) === null) { + tag = '[Object: null prototype]'; + } + return `${tag ? `${tag} ` : ''}{ ${Object.keys(value).map(key => `${key.toString()}: ${assert.deepEqual.format(value[key], seen)}`).join(', ')} }${usage.used ? ` as #${usage.id}` : ''}`; + default: + return typeof value; + } +}; + +assert.deepEqual._compare = (function () { + var EQUAL = 1; + var NOT_EQUAL = -1; + var UNKNOWN = 0; + + function deepEqual(a, b) { + return compareEquality(a, b) === EQUAL; + } + + function compareEquality(a, b, cache) { + return compareIf(a, b, isOptional, compareOptionality) + || compareIf(a, b, isPrimitiveEquatable, comparePrimitiveEquality) + || compareIf(a, b, isObjectEquatable, compareObjectEquality, cache) + || NOT_EQUAL; + } + + function compareIf(a, b, test, compare, cache) { + return !test(a) + ? !test(b) ? UNKNOWN : NOT_EQUAL + : !test(b) ? NOT_EQUAL : cacheComparison(a, b, compare, cache); + } + + function tryCompareStrictEquality(a, b) { + return a === b ? EQUAL : UNKNOWN; + } + + function tryCompareTypeOfEquality(a, b) { + return typeof a !== typeof b ? NOT_EQUAL : UNKNOWN; + } + + function tryCompareToStringTagEquality(a, b) { + var aTag = Symbol.toStringTag in a ? a[Symbol.toStringTag] : undefined; + var bTag = Symbol.toStringTag in b ? b[Symbol.toStringTag] : undefined; + return aTag !== bTag ? NOT_EQUAL : UNKNOWN; + } + + function isOptional(value) { + return value === undefined + || value === null; + } + + function compareOptionality(a, b) { + return tryCompareStrictEquality(a, b) + || NOT_EQUAL; + } + + function isPrimitiveEquatable(value) { + switch (typeof value) { + case 'string': + case 'number': + case 'bigint': + case 'boolean': + case 'symbol': + return true; + default: + return isBoxed(value); + } + } + + function comparePrimitiveEquality(a, b) { + if (isBoxed(a)) a = a.valueOf(); + if (isBoxed(b)) b = b.valueOf(); + return tryCompareStrictEquality(a, b) + || tryCompareTypeOfEquality(a, b) + || compareIf(a, b, isNaNEquatable, compareNaNEquality) + || NOT_EQUAL; + } + + function isNaNEquatable(value) { + return typeof value === 'number'; + } + + function compareNaNEquality(a, b) { + return isNaN(a) && isNaN(b) ? EQUAL : NOT_EQUAL; + } + + function isObjectEquatable(value) { + return typeof value === 'object'; + } + + function compareObjectEquality(a, b, cache) { + if (!cache) cache = new Map(); + return getCache(cache, a, b) + || setCache(cache, a, b, EQUAL) // consider equal for now + || cacheComparison(a, b, tryCompareStrictEquality, cache) + || cacheComparison(a, b, tryCompareToStringTagEquality, cache) + || compareIf(a, b, isValueOfEquatable, compareValueOfEquality) + || compareIf(a, b, isToStringEquatable, compareToStringEquality) + || compareIf(a, b, isArrayLikeEquatable, compareArrayLikeEquality, cache) + || compareIf(a, b, isStructurallyEquatable, compareStructuralEquality, cache) + || compareIf(a, b, isIterableEquatable, compareIterableEquality, cache) + || cacheComparison(a, b, fail, cache); + } + + function isBoxed(value) { + return value instanceof String + || value instanceof Number + || value instanceof Boolean + || typeof Symbol === 'function' && value instanceof Symbol + || typeof BigInt === 'function' && value instanceof BigInt; + } + + function isValueOfEquatable(value) { + return value instanceof Date; + } + + function compareValueOfEquality(a, b) { + return compareIf(a.valueOf(), b.valueOf(), isPrimitiveEquatable, comparePrimitiveEquality) + || NOT_EQUAL; + } + + function isToStringEquatable(value) { + return value instanceof RegExp; + } + + function compareToStringEquality(a, b) { + return compareIf(a.toString(), b.toString(), isPrimitiveEquatable, comparePrimitiveEquality) + || NOT_EQUAL; + } + + function isArrayLikeEquatable(value) { + return (Array.isArray ? Array.isArray(value) : value instanceof Array) + || (typeof Uint8Array === 'function' && value instanceof Uint8Array) + || (typeof Uint8ClampedArray === 'function' && value instanceof Uint8ClampedArray) + || (typeof Uint16Array === 'function' && value instanceof Uint16Array) + || (typeof Uint32Array === 'function' && value instanceof Uint32Array) + || (typeof Int8Array === 'function' && value instanceof Int8Array) + || (typeof Int16Array === 'function' && value instanceof Int16Array) + || (typeof Int32Array === 'function' && value instanceof Int32Array) + || (typeof Float32Array === 'function' && value instanceof Float32Array) + || (typeof Float64Array === 'function' && value instanceof Float64Array) + || (typeof BigUint64Array === 'function' && value instanceof BigUint64Array) + || (typeof BigInt64Array === 'function' && value instanceof BigInt64Array); + } + + function compareArrayLikeEquality(a, b, cache) { + if (a.length !== b.length) return NOT_EQUAL; + for (var i = 0; i < a.length; i++) { + if (compareEquality(a[i], b[i], cache) === NOT_EQUAL) { + return NOT_EQUAL; + } + } + return EQUAL; + } + + function isStructurallyEquatable(value) { + return !(typeof Promise === 'function' && value instanceof Promise // only comparable by reference + || typeof WeakMap === 'function' && value instanceof WeakMap // only comparable by reference + || typeof WeakSet === 'function' && value instanceof WeakSet // only comparable by reference + || typeof Map === 'function' && value instanceof Map // comparable via @@iterator + || typeof Set === 'function' && value instanceof Set); // comparable via @@iterator + } + + function compareStructuralEquality(a, b, cache) { + var aKeys = []; + for (var key in a) aKeys.push(key); + + var bKeys = []; + for (var key in b) bKeys.push(key); + + if (aKeys.length !== bKeys.length) { + return NOT_EQUAL; + } + + aKeys.sort(); + bKeys.sort(); + + for (var i = 0; i < aKeys.length; i++) { + var aKey = aKeys[i]; + var bKey = bKeys[i]; + if (compareEquality(aKey, bKey, cache) === NOT_EQUAL) { + return NOT_EQUAL; + } + if (compareEquality(a[aKey], b[bKey], cache) === NOT_EQUAL) { + return NOT_EQUAL; + } + } + + return compareIf(a, b, isIterableEquatable, compareIterableEquality, cache) + || EQUAL; + } + + function isIterableEquatable(value) { + return typeof Symbol === 'function' + && typeof value[Symbol.iterator] === 'function'; + } + + function compareIteratorEquality(a, b, cache) { + if (typeof Map === 'function' && a instanceof Map && b instanceof Map || + typeof Set === 'function' && a instanceof Set && b instanceof Set) { + if (a.size !== b.size) return NOT_EQUAL; // exit early if we detect a difference in size + } + + var ar, br; + while (true) { + ar = a.next(); + br = b.next(); + if (ar.done) { + if (br.done) return EQUAL; + if (b.return) b.return(); + return NOT_EQUAL; + } + if (br.done) { + if (a.return) a.return(); + return NOT_EQUAL; + } + if (compareEquality(ar.value, br.value, cache) === NOT_EQUAL) { + if (a.return) a.return(); + if (b.return) b.return(); + return NOT_EQUAL; + } + } + } + + function compareIterableEquality(a, b, cache) { + return compareIteratorEquality(a[Symbol.iterator](), b[Symbol.iterator](), cache); + } + + function cacheComparison(a, b, compare, cache) { + var result = compare(a, b, cache); + if (cache && (result === EQUAL || result === NOT_EQUAL)) { + setCache(cache, a, b, /** @type {EQUAL | NOT_EQUAL} */(result)); + } + return result; + } + + function fail() { + return NOT_EQUAL; + } + + function setCache(cache, left, right, result) { + var otherCache; + + otherCache = cache.get(left); + if (!otherCache) cache.set(left, otherCache = new Map()); + otherCache.set(right, result); + + otherCache = cache.get(right); + if (!otherCache) cache.set(right, otherCache = new Map()); + otherCache.set(left, result); + } + + function getCache(cache, left, right) { + var otherCache; + var result; + + otherCache = cache.get(left); + result = otherCache && otherCache.get(right); + if (result) return result; + + otherCache = cache.get(right); + result = otherCache && otherCache.get(left); + if (result) return result; + + return UNKNOWN; + } + + return deepEqual; +})(); + +// file: temporalHelpers.js +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: | + This defines helper objects and functions for testing Temporal. +defines: [TemporalHelpers] +features: [Symbol.species, Symbol.iterator, Temporal] +---*/ + +const ASCII_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/u; + +function formatPropertyName(propertyKey, objectName = "") { + switch (typeof propertyKey) { + case "symbol": + if (Symbol.keyFor(propertyKey) !== undefined) { + return `${objectName}[Symbol.for('${Symbol.keyFor(propertyKey)}')]`; + } else if (propertyKey.description.startsWith('Symbol.')) { + return `${objectName}[${propertyKey.description}]`; + } else { + return `${objectName}[Symbol('${propertyKey.description}')]` + } + case "string": + if (propertyKey !== String(Number(propertyKey))) { + if (ASCII_IDENTIFIER.test(propertyKey)) { + return objectName ? `${objectName}.${propertyKey}` : propertyKey; + } + return `${objectName}['${propertyKey.replace(/'/g, "\\'")}']` + } + // fall through + default: + // integer or string integer-index + return `${objectName}[${propertyKey}]`; + } +} + +const SKIP_SYMBOL = Symbol("Skip"); + +var TemporalHelpers = { + /* + * Codes and maximum lengths of months in the ISO 8601 calendar. + */ + ISOMonths: [ + { month: 1, monthCode: "M01", daysInMonth: 31 }, + { month: 2, monthCode: "M02", daysInMonth: 29 }, + { month: 3, monthCode: "M03", daysInMonth: 31 }, + { month: 4, monthCode: "M04", daysInMonth: 30 }, + { month: 5, monthCode: "M05", daysInMonth: 31 }, + { month: 6, monthCode: "M06", daysInMonth: 30 }, + { month: 7, monthCode: "M07", daysInMonth: 31 }, + { month: 8, monthCode: "M08", daysInMonth: 31 }, + { month: 9, monthCode: "M09", daysInMonth: 30 }, + { month: 10, monthCode: "M10", daysInMonth: 31 }, + { month: 11, monthCode: "M11", daysInMonth: 30 }, + { month: 12, monthCode: "M12", daysInMonth: 31 } + ], + + /* + * assertDuration(duration, years, ..., nanoseconds[, description]): + * + * Shorthand for asserting that each field of a Temporal.Duration is equal to + * an expected value. + */ + assertDuration(duration, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(duration instanceof Temporal.Duration, `${prefix}instanceof`); + assert.sameValue(duration.years, years, `${prefix}years result:`); + assert.sameValue(duration.months, months, `${prefix}months result:`); + assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`); + assert.sameValue(duration.days, days, `${prefix}days result:`); + assert.sameValue(duration.hours, hours, `${prefix}hours result:`); + assert.sameValue(duration.minutes, minutes, `${prefix}minutes result:`); + assert.sameValue(duration.seconds, seconds, `${prefix}seconds result:`); + assert.sameValue(duration.milliseconds, milliseconds, `${prefix}milliseconds result:`); + assert.sameValue(duration.microseconds, microseconds, `${prefix}microseconds result:`); + assert.sameValue(duration.nanoseconds, nanoseconds, `${prefix}nanoseconds result`); + }, + + /* + * assertDateDuration(duration, years, months, weeks, days, [, description]): + * + * Shorthand for asserting that each date field of a Temporal.Duration is + * equal to an expected value. + */ + assertDateDuration(duration, years, months, weeks, days, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(duration instanceof Temporal.Duration, `${prefix}instanceof`); + assert.sameValue(duration.years, years, `${prefix}years result:`); + assert.sameValue(duration.months, months, `${prefix}months result:`); + assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`); + assert.sameValue(duration.days, days, `${prefix}days result:`); + assert.sameValue(duration.hours, 0, `${prefix}hours result should be zero:`); + assert.sameValue(duration.minutes, 0, `${prefix}minutes result should be zero:`); + assert.sameValue(duration.seconds, 0, `${prefix}seconds result should be zero:`); + assert.sameValue(duration.milliseconds, 0, `${prefix}milliseconds result should be zero:`); + assert.sameValue(duration.microseconds, 0, `${prefix}microseconds result should be zero:`); + assert.sameValue(duration.nanoseconds, 0, `${prefix}nanoseconds result should be zero:`); + }, + + /* + * assertDurationsEqual(actual, expected[, description]): + * + * Shorthand for asserting that each field of a Temporal.Duration is equal to + * the corresponding field in another Temporal.Duration. + */ + assertDurationsEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.Duration, `${prefix}expected value should be a Temporal.Duration`); + TemporalHelpers.assertDuration(actual, expected.years, expected.months, expected.weeks, expected.days, expected.hours, expected.minutes, expected.seconds, expected.milliseconds, expected.microseconds, expected.nanoseconds, description); + }, + + /* + * assertInstantsEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.Instants are of the correct type + * and equal according to their equals() methods. + */ + assertInstantsEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.Instant, `${prefix}expected value should be a Temporal.Instant`); + assert(actual instanceof Temporal.Instant, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + }, + + /* + * assertPlainDate(date, year, ..., nanosecond[, description[, era, eraYear]]): + * + * Shorthand for asserting that each field of a Temporal.PlainDate is equal to + * an expected value. (Except the `calendar` property, since callers may want + * to assert either object equality with an object they put in there, or the + * value of date.calendarId.) + */ + assertPlainDate(date, year, month, monthCode, day, description = "", era = undefined, eraYear = undefined) { + const prefix = description ? `${description}: ` : ""; + assert(date instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert.sameValue(date.era, era, `${prefix}era result:`); + assert.sameValue(date.eraYear, eraYear, `${prefix}eraYear result:`); + assert.sameValue(date.year, year, `${prefix}year result:`); + assert.sameValue(date.month, month, `${prefix}month result:`); + assert.sameValue(date.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(date.day, day, `${prefix}day result:`); + }, + + /* + * assertPlainDateTime(datetime, year, ..., nanosecond[, description[, era, eraYear]]): + * + * Shorthand for asserting that each field of a Temporal.PlainDateTime is + * equal to an expected value. (Except the `calendar` property, since callers + * may want to assert either object equality with an object they put in there, + * or the value of datetime.calendarId.) + */ + assertPlainDateTime(datetime, year, month, monthCode, day, hour, minute, second, millisecond, microsecond, nanosecond, description = "", era = undefined, eraYear = undefined) { + const prefix = description ? `${description}: ` : ""; + assert(datetime instanceof Temporal.PlainDateTime, `${prefix}instanceof`); + assert.sameValue(datetime.era, era, `${prefix}era result:`); + assert.sameValue(datetime.eraYear, eraYear, `${prefix}eraYear result:`); + assert.sameValue(datetime.year, year, `${prefix}year result:`); + assert.sameValue(datetime.month, month, `${prefix}month result:`); + assert.sameValue(datetime.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(datetime.day, day, `${prefix}day result:`); + assert.sameValue(datetime.hour, hour, `${prefix}hour result:`); + assert.sameValue(datetime.minute, minute, `${prefix}minute result:`); + assert.sameValue(datetime.second, second, `${prefix}second result:`); + assert.sameValue(datetime.millisecond, millisecond, `${prefix}millisecond result:`); + assert.sameValue(datetime.microsecond, microsecond, `${prefix}microsecond result:`); + assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); + }, + + /* + * assertPlainDateTimesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDateTimes are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDateTimesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDateTime, `${prefix}expected value should be a Temporal.PlainDateTime`); + assert(actual instanceof Temporal.PlainDateTime, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.getISOFields().calendar, + expected.getISOFields().calendar, + `${prefix}calendar same value:` + ); + }, + + /* + * assertPlainMonthDay(monthDay, monthCode, day[, description [, referenceISOYear]]): + * + * Shorthand for asserting that each field of a Temporal.PlainMonthDay is + * equal to an expected value. (Except the `calendar` property, since callers + * may want to assert either object equality with an object they put in there, + * or the value of monthDay.calendarId().) + */ + assertPlainMonthDay(monthDay, monthCode, day, description = "", referenceISOYear = 1972) { + const prefix = description ? `${description}: ` : ""; + assert(monthDay instanceof Temporal.PlainMonthDay, `${prefix}instanceof`); + assert.sameValue(monthDay.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(monthDay.day, day, `${prefix}day result:`); + assert.sameValue(monthDay.getISOFields().isoYear, referenceISOYear, `${prefix}referenceISOYear result:`); + }, + + /* + * assertPlainTime(time, hour, ..., nanosecond[, description]): + * + * Shorthand for asserting that each field of a Temporal.PlainTime is equal to + * an expected value. + */ + assertPlainTime(time, hour, minute, second, millisecond, microsecond, nanosecond, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(time instanceof Temporal.PlainTime, `${prefix}instanceof`); + assert.sameValue(time.hour, hour, `${prefix}hour result:`); + assert.sameValue(time.minute, minute, `${prefix}minute result:`); + assert.sameValue(time.second, second, `${prefix}second result:`); + assert.sameValue(time.millisecond, millisecond, `${prefix}millisecond result:`); + assert.sameValue(time.microsecond, microsecond, `${prefix}microsecond result:`); + assert.sameValue(time.nanosecond, nanosecond, `${prefix}nanosecond result:`); + }, + + /* + * assertPlainTimesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainTimes are of the correct + * type and equal according to their equals() methods. + */ + assertPlainTimesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainTime, `${prefix}expected value should be a Temporal.PlainTime`); + assert(actual instanceof Temporal.PlainTime, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + }, + + /* + * assertPlainYearMonth(yearMonth, year, month, monthCode[, description[, era, eraYear, referenceISODay]]): + * + * Shorthand for asserting that each field of a Temporal.PlainYearMonth is + * equal to an expected value. (Except the `calendar` property, since callers + * may want to assert either object equality with an object they put in there, + * or the value of yearMonth.calendarId.) + */ + assertPlainYearMonth(yearMonth, year, month, monthCode, description = "", era = undefined, eraYear = undefined, referenceISODay = 1) { + const prefix = description ? `${description}: ` : ""; + assert(yearMonth instanceof Temporal.PlainYearMonth, `${prefix}instanceof`); + assert.sameValue(yearMonth.era, era, `${prefix}era result:`); + assert.sameValue(yearMonth.eraYear, eraYear, `${prefix}eraYear result:`); + assert.sameValue(yearMonth.year, year, `${prefix}year result:`); + assert.sameValue(yearMonth.month, month, `${prefix}month result:`); + assert.sameValue(yearMonth.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(yearMonth.getISOFields().isoDay, referenceISODay, `${prefix}referenceISODay result:`); + }, + + /* + * assertZonedDateTimesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.ZonedDateTimes are of the correct + * type, equal according to their equals() methods, and additionally that + * their time zones and calendar internal slots are the same value. + */ + assertZonedDateTimesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.ZonedDateTime, `${prefix}expected value should be a Temporal.ZonedDateTime`); + assert(actual instanceof Temporal.ZonedDateTime, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue(actual.timeZone, expected.timeZone, `${prefix}time zone same value:`); + assert.sameValue( + actual.getISOFields().calendar, + expected.getISOFields().calendar, + `${prefix}calendar same value:` + ); + }, + + /* + * assertUnreachable(description): + * + * Helper for asserting that code is not executed. This is useful for + * assertions that methods of user calendars and time zones are not called. + */ + assertUnreachable(description) { + let message = "This code should not be executed"; + if (description) { + message = `${message}: ${description}`; + } + throw new Test262Error(message); + }, + + /* + * checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls): + * + * When an options object with a largestUnit property is synthesized inside + * Temporal and passed to user code such as calendar.dateUntil(), the value of + * the largestUnit property should be in the singular form, even if the input + * was given in the plural form. + * (This doesn't apply when the options object is passed through verbatim.) + * + * func(calendar, largestUnit, index) is the operation under test. It's called + * with an instance of a calendar that keeps track of which largestUnit is + * passed to dateUntil(), each key of expectedLargestUnitCalls in turn, and + * the key's numerical index in case the function needs to generate test data + * based on the index. At the end, the actual values passed to dateUntil() are + * compared with the array values of expectedLargestUnitCalls. + */ + checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls) { + const actual = []; + + class DateUntilOptionsCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateUntil(earlier, later, options) { + actual.push(options.largestUnit); + return super.dateUntil(earlier, later, options); + } + + toString() { + return "date-until-options"; + } + } + + const calendar = new DateUntilOptionsCalendar(); + Object.entries(expectedLargestUnitCalls).forEach(([largestUnit, expected], index) => { + func(calendar, largestUnit, index); + assert.compareArray(actual, expected, `largestUnit passed to calendar.dateUntil() for largestUnit ${largestUnit}`); + actual.splice(0); // empty it for the next check + }); + }, + + /* + * checkPlainDateTimeConversionFastPath(func): + * + * ToTemporalDate and ToTemporalTime should both, if given a + * Temporal.PlainDateTime instance, convert to the desired type by reading the + * PlainDateTime's internal slots, rather than calling any getters. + * + * func(datetime, calendar) is the actual operation to test, that must + * internally call the abstract operation ToTemporalDate or ToTemporalTime. + * It is passed a Temporal.PlainDateTime instance, as well as the instance's + * calendar object (so that it doesn't have to call the calendar getter itself + * if it wants to make any assertions about the calendar.) + */ + checkPlainDateTimeConversionFastPath(func, message = "checkPlainDateTimeConversionFastPath") { + const actual = []; + const expected = []; + + const calendar = new Temporal.Calendar("iso8601"); + const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar); + const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDateTime.prototype); + ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => { + Object.defineProperty(datetime, property, { + get() { + actual.push(`get ${formatPropertyName(property)}`); + const value = prototypeDescrs[property].get.call(this); + return { + toString() { + actual.push(`toString ${formatPropertyName(property)}`); + return value.toString(); + }, + valueOf() { + actual.push(`valueOf ${formatPropertyName(property)}`); + return value; + }, + }; + }, + }); + }); + Object.defineProperty(datetime, "calendar", { + get() { + actual.push("get calendar"); + return calendar; + }, + }); + + func(datetime, calendar); + assert.compareArray(actual, expected, `${message}: property getters not called`); + }, + + /* + * Check that an options bag that accepts units written in the singular form, + * also accepts the same units written in the plural form. + * func(unit) should call the method with the appropriate options bag + * containing unit as a value. This will be called twice for each element of + * validSingularUnits, once with singular and once with plural, and the + * results of each pair should be the same (whether a Temporal object or a + * primitive value.) + */ + checkPluralUnitsAccepted(func, validSingularUnits) { + const plurals = { + year: 'years', + month: 'months', + week: 'weeks', + day: 'days', + hour: 'hours', + minute: 'minutes', + second: 'seconds', + millisecond: 'milliseconds', + microsecond: 'microseconds', + nanosecond: 'nanoseconds', + }; + + validSingularUnits.forEach((unit) => { + const singularValue = func(unit); + const pluralValue = func(plurals[unit]); + const desc = `Plural ${plurals[unit]} produces the same result as singular ${unit}`; + if (singularValue instanceof Temporal.Duration) { + TemporalHelpers.assertDurationsEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.Instant) { + TemporalHelpers.assertInstantsEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.PlainDateTime) { + TemporalHelpers.assertPlainDateTimesEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.PlainTime) { + TemporalHelpers.assertPlainTimesEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.ZonedDateTime) { + TemporalHelpers.assertZonedDateTimesEqual(pluralValue, singularValue, desc); + } else { + assert.sameValue(pluralValue, singularValue); + } + }); + }, + + /* + * checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc): + * + * Checks the type handling of the roundingIncrement option. + * checkFunc(roundingIncrement) is a function which takes the value of + * roundingIncrement to test, and calls the method under test with it, + * returning the result. assertTrueResultFunc(result, description) should + * assert that result is the expected result with roundingIncrement: true, and + * assertObjectResultFunc(result, description) should assert that result is + * the expected result with roundingIncrement being an object with a valueOf() + * method. + */ + checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc) { + // null converts to 0, which is out of range + assert.throws(RangeError, () => checkFunc(null), "null"); + // Booleans convert to either 0 or 1, and 1 is allowed + const trueResult = checkFunc(true); + assertTrueResultFunc(trueResult, "true"); + assert.throws(RangeError, () => checkFunc(false), "false"); + // Symbols and BigInts cannot convert to numbers + assert.throws(TypeError, () => checkFunc(Symbol()), "symbol"); + assert.throws(TypeError, () => checkFunc(2n), "bigint"); + + // Objects prefer their valueOf() methods when converting to a number + assert.throws(RangeError, () => checkFunc({}), "plain object"); + + const expected = [ + "get roundingIncrement.valueOf", + "call roundingIncrement.valueOf", + ]; + const actual = []; + const observer = TemporalHelpers.toPrimitiveObserver(actual, 2, "roundingIncrement"); + const objectResult = checkFunc(observer); + assertObjectResultFunc(objectResult, "object with valueOf"); + assert.compareArray(actual, expected, "order of operations"); + }, + + /* + * checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc): + * + * Checks the type handling of a string option, of which there are several in + * Temporal. + * propertyName is the name of the option, and value is the value that + * assertFunc should expect it to have. + * checkFunc(value) is a function which takes the value of the option to test, + * and calls the method under test with it, returning the result. + * assertFunc(result, description) should assert that result is the expected + * result with the option value being an object with a toString() method + * which returns the given value. + */ + checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc) { + // null converts to the string "null", which is an invalid string value + assert.throws(RangeError, () => checkFunc(null), "null"); + // Booleans convert to the strings "true" or "false", which are invalid + assert.throws(RangeError, () => checkFunc(true), "true"); + assert.throws(RangeError, () => checkFunc(false), "false"); + // Symbols cannot convert to strings + assert.throws(TypeError, () => checkFunc(Symbol()), "symbol"); + // Numbers convert to strings which are invalid + assert.throws(RangeError, () => checkFunc(2), "number"); + // BigInts convert to strings which are invalid + assert.throws(RangeError, () => checkFunc(2n), "bigint"); + + // Objects prefer their toString() methods when converting to a string + assert.throws(RangeError, () => checkFunc({}), "plain object"); + + const expected = [ + `get ${propertyName}.toString`, + `call ${propertyName}.toString`, + ]; + const actual = []; + const observer = TemporalHelpers.toPrimitiveObserver(actual, value, propertyName); + const result = checkFunc(observer); + assertFunc(result, "object with toString"); + assert.compareArray(actual, expected, "order of operations"); + }, + + /* + * checkSubclassingIgnored(construct, constructArgs, method, methodArgs, + * resultAssertions): + * + * Methods of Temporal classes that return a new instance of the same class, + * must not take the constructor of a subclass into account, nor the @@species + * property. This helper runs tests to ensure this. + * + * construct(...constructArgs) must yield a valid instance of the Temporal + * class. instance[method](...methodArgs) is the method call under test, which + * must also yield a valid instance of the same Temporal class, not a + * subclass. See below for the individual tests that this runs. + * resultAssertions() is a function that performs additional assertions on the + * instance returned by the method under test. + */ + checkSubclassingIgnored(...args) { + this.checkSubclassConstructorNotObject(...args); + this.checkSubclassConstructorUndefined(...args); + this.checkSubclassConstructorThrows(...args); + this.checkSubclassConstructorNotCalled(...args); + this.checkSubclassSpeciesInvalidResult(...args); + this.checkSubclassSpeciesNotAConstructor(...args); + this.checkSubclassSpeciesNull(...args); + this.checkSubclassSpeciesUndefined(...args); + this.checkSubclassSpeciesThrows(...args); + }, + + /* + * Checks that replacing the 'constructor' property of the instance with + * various primitive values does not affect the returned new instance. + */ + checkSubclassConstructorNotObject(construct, constructArgs, method, methodArgs, resultAssertions) { + function check(value, description) { + const instance = new construct(...constructArgs); + instance.constructor = value; + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); + resultAssertions(result); + } + + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "Symbol"); + check(7, "number"); + check(7n, "bigint"); + }, + + /* + * Checks that replacing the 'constructor' property of the subclass with + * undefined does not affect the returned new instance. + */ + checkSubclassConstructorUndefined(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + MySubclass.prototype.constructor = undefined; + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Checks that making the 'constructor' property of the instance throw when + * called does not affect the returned new instance. + */ + checkSubclassConstructorThrows(construct, constructArgs, method, methodArgs, resultAssertions) { + function CustomError() {} + const instance = new construct(...constructArgs); + Object.defineProperty(instance, "constructor", { + get() { + throw new CustomError(); + } + }); + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Checks that when subclassing, the subclass constructor is not called by + * the method under test. + */ + checkSubclassConstructorNotCalled(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that the constructor's @@species property is ignored when it's a + * constructor that returns a non-object value. + */ + checkSubclassSpeciesInvalidResult(construct, constructArgs, method, methodArgs, resultAssertions) { + function check(value, description) { + const instance = new construct(...constructArgs); + instance.constructor = { + [Symbol.species]: function() { + return value; + }, + }; + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); + resultAssertions(result); + } + + check(undefined, "undefined"); + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "Symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "plain object"); + }, + + /* + * Check that the constructor's @@species property is ignored when it's not a + * constructor. + */ + checkSubclassSpeciesNotAConstructor(construct, constructArgs, method, methodArgs, resultAssertions) { + function check(value, description) { + const instance = new construct(...constructArgs); + instance.constructor = { + [Symbol.species]: value, + }; + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); + resultAssertions(result); + } + + check(true, "true"); + check("test", "string"); + check(Symbol(), "Symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "plain object"); + }, + + /* + * Check that the constructor's @@species property is ignored when it's null. + */ + checkSubclassSpeciesNull(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + MySubclass.prototype.constructor = { + [Symbol.species]: null, + }; + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that the constructor's @@species property is ignored when it's + * undefined. + */ + checkSubclassSpeciesUndefined(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + MySubclass.prototype.constructor = { + [Symbol.species]: undefined, + }; + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that the constructor's @@species property is ignored when it throws, + * i.e. it is not called at all. + */ + checkSubclassSpeciesThrows(construct, constructArgs, method, methodArgs, resultAssertions) { + function CustomError() {} + + const instance = new construct(...constructArgs); + instance.constructor = { + get [Symbol.species]() { + throw new CustomError(); + }, + }; + + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + }, + + /* + * checkSubclassingIgnoredStatic(construct, method, methodArgs, resultAssertions): + * + * Static methods of Temporal classes that return a new instance of the class, + * must not use the this-value as a constructor. This helper runs tests to + * ensure this. + * + * construct[method](...methodArgs) is the static method call under test, and + * must yield a valid instance of the Temporal class, not a subclass. See + * below for the individual tests that this runs. + * resultAssertions() is a function that performs additional assertions on the + * instance returned by the method under test. + */ + checkSubclassingIgnoredStatic(...args) { + this.checkStaticInvalidReceiver(...args); + this.checkStaticReceiverNotCalled(...args); + this.checkThisValueNotCalled(...args); + }, + + /* + * Check that calling the static method with a receiver that's not callable, + * still calls the intrinsic constructor. + */ + checkStaticInvalidReceiver(construct, method, methodArgs, resultAssertions) { + function check(value, description) { + const result = construct[method].apply(value, methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + } + + check(undefined, "undefined"); + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "Non-callable object"); + }, + + /* + * Check that calling the static method with a receiver that returns a value + * that's not callable, still calls the intrinsic constructor. + */ + checkStaticReceiverNotCalled(construct, method, methodArgs, resultAssertions) { + function check(value, description) { + const receiver = function () { + return value; + }; + const result = construct[method].apply(receiver, methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + } + + check(undefined, "undefined"); + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "Non-callable object"); + }, + + /* + * Check that the receiver isn't called. + */ + checkThisValueNotCalled(construct, method, methodArgs, resultAssertions) { + let called = false; + + class MySubclass extends construct { + constructor(...args) { + called = true; + super(...args); + } + } + + const result = MySubclass[method](...methodArgs); + assert.sameValue(called, false); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that any iterable returned from a custom time zone's + * getPossibleInstantsFor() method is exhausted. + * The custom time zone object is passed in to func(). + * expected is an array of strings representing the expected calls to the + * getPossibleInstantsFor() method. The PlainDateTimes that it is called with, + * are compared (using their toString() results) with the array. + */ + checkTimeZonePossibleInstantsIterable(func, expected) { + // A custom time zone that returns an iterable instead of an array from its + // getPossibleInstantsFor() method, and for testing purposes skips + // 00:00-01:00 UTC on January 1, 2030, and repeats 00:00-01:00 UTC+1 on + // January 3, 2030. Otherwise identical to the UTC time zone. + class TimeZonePossibleInstantsIterable extends Temporal.TimeZone { + constructor() { + super("UTC"); + this.getPossibleInstantsForCallCount = 0; + this.getPossibleInstantsForCalledWith = []; + this.getPossibleInstantsForReturns = []; + this.iteratorExhausted = []; + } + + toString() { + return "Custom/Iterable"; + } + + getOffsetNanosecondsFor(instant) { + if (Temporal.Instant.compare(instant, "2030-01-01T00:00Z") >= 0 && + Temporal.Instant.compare(instant, "2030-01-03T01:00Z") < 0) { + return 3600_000_000_000; + } else { + return 0; + } + } + + getPossibleInstantsFor(dateTime) { + this.getPossibleInstantsForCallCount++; + this.getPossibleInstantsForCalledWith.push(dateTime); + + // Fake DST transition + let retval = super.getPossibleInstantsFor(dateTime); + if (dateTime.toPlainDate().equals("2030-01-01") && dateTime.hour === 0) { + retval = []; + } else if (dateTime.toPlainDate().equals("2030-01-03") && dateTime.hour === 0) { + retval.push(retval[0].subtract({ hours: 1 })); + } else if (dateTime.year === 2030 && dateTime.month === 1 && dateTime.day >= 1 && dateTime.day <= 2) { + retval[0] = retval[0].subtract({ hours: 1 }); + } + + this.getPossibleInstantsForReturns.push(retval); + this.iteratorExhausted.push(false); + return { + callIndex: this.getPossibleInstantsForCallCount - 1, + timeZone: this, + *[Symbol.iterator]() { + yield* this.timeZone.getPossibleInstantsForReturns[this.callIndex]; + this.timeZone.iteratorExhausted[this.callIndex] = true; + }, + }; + } + } + + const timeZone = new TimeZonePossibleInstantsIterable(); + func(timeZone); + + assert.sameValue(timeZone.getPossibleInstantsForCallCount, expected.length, "getPossibleInstantsFor() method called correct number of times"); + + for (let index = 0; index < expected.length; index++) { + assert.sameValue(timeZone.getPossibleInstantsForCalledWith[index].toString(), expected[index], "getPossibleInstantsFor() called with expected PlainDateTime"); + assert(timeZone.iteratorExhausted[index], "iterated through the whole iterable"); + } + }, + + /* + * Check that any calendar-carrying Temporal object has its [[Calendar]] + * internal slot read by ToTemporalCalendar, and does not fetch the calendar + * by calling getters. + * The custom calendar object is passed in to func() so that it can do its + * own additional assertions involving the calendar if necessary. (Sometimes + * there is nothing to assert as the calendar isn't stored anywhere that can + * be asserted about.) + */ + checkToTemporalCalendarFastPath(func) { + class CalendarFastPathCheck extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateFromFields(...args) { + return super.dateFromFields(...args).withCalendar(this); + } + + monthDayFromFields(...args) { + const { isoYear, isoMonth, isoDay } = super.monthDayFromFields(...args).getISOFields(); + return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); + } + + yearMonthFromFields(...args) { + const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields(...args).getISOFields(); + return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); + } + + toString() { + return "fast-path-check"; + } + } + const calendar = new CalendarFastPathCheck(); + + const plainDate = new Temporal.PlainDate(2000, 5, 2, calendar); + const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar); + const plainMonthDay = new Temporal.PlainMonthDay(5, 2, calendar); + const plainYearMonth = new Temporal.PlainYearMonth(2000, 5, calendar); + const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + + [plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((temporalObject) => { + const actual = []; + const expected = []; + + Object.defineProperty(temporalObject, "calendar", { + get() { + actual.push("get calendar"); + return calendar; + }, + }); + + func(temporalObject, calendar); + assert.compareArray(actual, expected, "calendar getter not called"); + }); + }, + + checkToTemporalInstantFastPath(func) { + const actual = []; + const expected = []; + + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + Object.defineProperty(datetime, 'toString', { + get() { + actual.push("get toString"); + return function (options) { + actual.push("call toString"); + return Temporal.ZonedDateTime.prototype.toString.call(this, options); + }; + }, + }); + + func(datetime); + assert.compareArray(actual, expected, "toString not called"); + }, + + checkToTemporalPlainDateTimeFastPath(func) { + const actual = []; + const expected = []; + + const calendar = new Temporal.Calendar("iso8601"); + const date = new Temporal.PlainDate(2000, 5, 2, calendar); + const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDate.prototype); + ["year", "month", "monthCode", "day"].forEach((property) => { + Object.defineProperty(date, property, { + get() { + actual.push(`get ${formatPropertyName(property)}`); + const value = prototypeDescrs[property].get.call(this); + return TemporalHelpers.toPrimitiveObserver(actual, value, property); + }, + }); + }); + ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => { + Object.defineProperty(date, property, { + get() { + actual.push(`get ${formatPropertyName(property)}`); + return undefined; + }, + }); + }); + Object.defineProperty(date, "calendar", { + get() { + actual.push("get calendar"); + return calendar; + }, + }); + + func(date, calendar); + assert.compareArray(actual, expected, "property getters not called"); + }, + + /* + * A custom calendar used in prototype pollution checks. Verifies that the + * fromFields methods are always called with a null-prototype fields object. + */ + calendarCheckFieldsPrototypePollution() { + class CalendarCheckFieldsPrototypePollution extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateFromFieldsCallCount = 0; + this.yearMonthFromFieldsCallCount = 0; + this.monthDayFromFieldsCallCount = 0; + } + + // toString must remain "iso8601", so that some methods don't throw due to + // incompatible calendars + + dateFromFields(fields, options = {}) { + this.dateFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "dateFromFields should be called with null-prototype fields object"); + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options = {}) { + this.yearMonthFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "yearMonthFromFields should be called with null-prototype fields object"); + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options = {}) { + this.monthDayFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "monthDayFromFields should be called with null-prototype fields object"); + return super.monthDayFromFields(fields, options); + } + } + + return new CalendarCheckFieldsPrototypePollution(); + }, + + /* + * A custom calendar used in prototype pollution checks. Verifies that the + * mergeFields() method is always called with null-prototype fields objects. + */ + calendarCheckMergeFieldsPrototypePollution() { + class CalendarCheckMergeFieldsPrototypePollution extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.mergeFieldsCallCount = 0; + } + + toString() { + return "merge-fields-null-proto"; + } + + mergeFields(fields, additionalFields) { + this.mergeFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "mergeFields should be called with null-prototype fields object (first argument)"); + assert.sameValue(Object.getPrototypeOf(additionalFields), null, "mergeFields should be called with null-prototype fields object (second argument)"); + return super.mergeFields(fields, additionalFields); + } + } + + return new CalendarCheckMergeFieldsPrototypePollution(); + }, + + /* + * A custom calendar used in prototype pollution checks. Verifies that methods + * are always called with a null-prototype options object. + */ + calendarCheckOptionsPrototypePollution() { + class CalendarCheckOptionsPrototypePollution extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.yearMonthFromFieldsCallCount = 0; + this.dateUntilCallCount = 0; + } + + toString() { + return "options-null-proto"; + } + + yearMonthFromFields(fields, options) { + this.yearMonthFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(options), null, "yearMonthFromFields should be called with null-prototype options"); + return super.yearMonthFromFields(fields, options); + } + + dateUntil(one, two, options) { + this.dateUntilCallCount++; + assert.sameValue(Object.getPrototypeOf(options), null, "dateUntil should be called with null-prototype options"); + return super.dateUntil(one, two, options); + } + } + + return new CalendarCheckOptionsPrototypePollution(); + }, + + /* + * A custom calendar that asserts its dateAdd() method is called with the + * options parameter having the value undefined. + */ + calendarDateAddUndefinedOptions() { + class CalendarDateAddUndefinedOptions extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateAddCallCount = 0; + } + + toString() { + return "dateadd-undef-options"; + } + + dateAdd(date, duration, options) { + this.dateAddCallCount++; + assert.sameValue(options, undefined, "dateAdd shouldn't be called with options"); + return super.dateAdd(date, duration, options); + } + } + return new CalendarDateAddUndefinedOptions(); + }, + + /* + * A custom calendar that asserts its dateAdd() method is called with a + * PlainDate instance. Optionally, it also asserts that the PlainDate instance + * is the specific object `this.specificPlainDate`, if it is set by the + * calling code. + */ + calendarDateAddPlainDateInstance() { + class CalendarDateAddPlainDateInstance extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateAddCallCount = 0; + this.specificPlainDate = undefined; + } + + toString() { + return "dateadd-plain-date-instance"; + } + + dateFromFields(...args) { + return super.dateFromFields(...args).withCalendar(this); + } + + dateAdd(date, duration, options) { + this.dateAddCallCount++; + assert(date instanceof Temporal.PlainDate, "dateAdd() should be called with a PlainDate instance"); + if (this.dateAddCallCount === 1 && this.specificPlainDate) { + assert.sameValue(date, this.specificPlainDate, `dateAdd() should be called first with the specific PlainDate instance ${this.specificPlainDate}`); + } + return super.dateAdd(date, duration, options).withCalendar(this); + } + } + return new CalendarDateAddPlainDateInstance(); + }, + + /* + * A custom calendar that returns an iterable instead of an array from its + * fields() method, otherwise identical to the ISO calendar. + */ + calendarFieldsIterable() { + class CalendarFieldsIterable extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.fieldsCallCount = 0; + this.fieldsCalledWith = []; + this.iteratorExhausted = []; + } + + toString() { + return "fields-iterable"; + } + + fields(fieldNames) { + this.fieldsCallCount++; + this.fieldsCalledWith.push(fieldNames.slice()); + this.iteratorExhausted.push(false); + return { + callIndex: this.fieldsCallCount - 1, + calendar: this, + *[Symbol.iterator]() { + yield* this.calendar.fieldsCalledWith[this.callIndex]; + this.calendar.iteratorExhausted[this.callIndex] = true; + }, + }; + } + } + return new CalendarFieldsIterable(); + }, + + /* + * A custom calendar that asserts its ...FromFields() methods are called with + * the options parameter having the value undefined. + */ + calendarFromFieldsUndefinedOptions() { + class CalendarFromFieldsUndefinedOptions extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateFromFieldsCallCount = 0; + this.monthDayFromFieldsCallCount = 0; + this.yearMonthFromFieldsCallCount = 0; + } + + toString() { + return "from-fields-undef-options"; + } + + dateFromFields(fields, options) { + this.dateFromFieldsCallCount++; + assert.sameValue(options, undefined, "dateFromFields shouldn't be called with options"); + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options) { + this.yearMonthFromFieldsCallCount++; + assert.sameValue(options, undefined, "yearMonthFromFields shouldn't be called with options"); + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options) { + this.monthDayFromFieldsCallCount++; + assert.sameValue(options, undefined, "monthDayFromFields shouldn't be called with options"); + return super.monthDayFromFields(fields, options); + } + } + return new CalendarFromFieldsUndefinedOptions(); + }, + + /* + * A custom calendar that modifies the fields object passed in to + * dateFromFields, sabotaging its time properties. + */ + calendarMakeInfinityTime() { + class CalendarMakeInfinityTime extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateFromFields(fields, options) { + const retval = super.dateFromFields(fields, options); + fields.hour = Infinity; + fields.minute = Infinity; + fields.second = Infinity; + fields.millisecond = Infinity; + fields.microsecond = Infinity; + fields.nanosecond = Infinity; + return retval; + } + } + return new CalendarMakeInfinityTime(); + }, + + /* + * A custom calendar that defines getters on the fields object passed into + * dateFromFields that throw, sabotaging its time properties. + */ + calendarMakeInvalidGettersTime() { + class CalendarMakeInvalidGettersTime extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateFromFields(fields, options) { + const retval = super.dateFromFields(fields, options); + const throwingDescriptor = { + get() { + throw new Test262Error("reading a sabotaged time field"); + }, + }; + Object.defineProperties(fields, { + hour: throwingDescriptor, + minute: throwingDescriptor, + second: throwingDescriptor, + millisecond: throwingDescriptor, + microsecond: throwingDescriptor, + nanosecond: throwingDescriptor, + }); + return retval; + } + } + return new CalendarMakeInvalidGettersTime(); + }, + + /* + * A custom calendar whose mergeFields() method returns a proxy object with + * all of its Get and HasProperty operations observable, as well as adding a + * "shouldNotBeCopied": true property. + */ + calendarMergeFieldsGetters() { + class CalendarMergeFieldsGetters extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.mergeFieldsReturnOperations = []; + } + + toString() { + return "merge-fields-getters"; + } + + dateFromFields(fields, options) { + assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied"); + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options) { + assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied"); + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options) { + assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied"); + return super.monthDayFromFields(fields, options); + } + + mergeFields(fields, additionalFields) { + const retval = super.mergeFields(fields, additionalFields); + retval._calendar = this; + retval.shouldNotBeCopied = true; + return new Proxy(retval, { + get(target, key) { + target._calendar.mergeFieldsReturnOperations.push(`get ${key}`); + const result = target[key]; + if (result === undefined) { + return undefined; + } + return TemporalHelpers.toPrimitiveObserver(target._calendar.mergeFieldsReturnOperations, result, key); + }, + has(target, key) { + target._calendar.mergeFieldsReturnOperations.push(`has ${key}`); + return key in target; + }, + }); + } + } + return new CalendarMergeFieldsGetters(); + }, + + /* + * A custom calendar whose mergeFields() method returns a primitive value, + * given by @primitive, and which records the number of calls made to its + * dateFromFields(), yearMonthFromFields(), and monthDayFromFields() methods. + */ + calendarMergeFieldsReturnsPrimitive(primitive) { + class CalendarMergeFieldsPrimitive extends Temporal.Calendar { + constructor(mergeFieldsReturnValue) { + super("iso8601"); + this._mergeFieldsReturnValue = mergeFieldsReturnValue; + this.dateFromFieldsCallCount = 0; + this.monthDayFromFieldsCallCount = 0; + this.yearMonthFromFieldsCallCount = 0; + } + + toString() { + return "merge-fields-primitive"; + } + + dateFromFields(fields, options) { + this.dateFromFieldsCallCount++; + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options) { + this.yearMonthFromFieldsCallCount++; + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options) { + this.monthDayFromFieldsCallCount++; + return super.monthDayFromFields(fields, options); + } + + mergeFields() { + return this._mergeFieldsReturnValue; + } + } + return new CalendarMergeFieldsPrimitive(primitive); + }, + + /* + * A custom calendar whose fields() method returns the same value as the + * iso8601 calendar, with the addition of extraFields provided as parameter. + */ + calendarWithExtraFields(fields) { + class CalendarWithExtraFields extends Temporal.Calendar { + constructor(extraFields) { + super("iso8601"); + this._extraFields = extraFields; + } + + fields(fieldNames) { + return super.fields(fieldNames).concat(this._extraFields); + } + } + + return new CalendarWithExtraFields(fields); + }, + + /* + * crossDateLineTimeZone(): + * + * This returns an instance of a custom time zone class that implements one + * single transition where the time zone moves from one side of the + * International Date Line to the other, for the purpose of testing time zone + * calculations without depending on system time zone data. + * + * The transition occurs at epoch second 1325239200 and goes from offset + * -10:00 to +14:00. In other words, the time zone skips the whole calendar + * day of 2011-12-30. This is the same as the real-life transition in the + * Pacific/Apia time zone. + */ + crossDateLineTimeZone() { + const { compare } = Temporal.PlainDate; + const skippedDay = new Temporal.PlainDate(2011, 12, 30); + const transitionEpoch = 1325239200_000_000_000n; + const beforeOffset = new Temporal.TimeZone("-10:00"); + const afterOffset = new Temporal.TimeZone("+14:00"); + + class CrossDateLineTimeZone extends Temporal.TimeZone { + constructor() { + super("+14:00"); + } + + getOffsetNanosecondsFor(instant) { + if (instant.epochNanoseconds < transitionEpoch) { + return beforeOffset.getOffsetNanosecondsFor(instant); + } + return afterOffset.getOffsetNanosecondsFor(instant); + } + + getPossibleInstantsFor(datetime) { + const comparison = compare(datetime.toPlainDate(), skippedDay); + if (comparison === 0) { + return []; + } + if (comparison < 0) { + return [beforeOffset.getInstantFor(datetime)]; + } + return [afterOffset.getInstantFor(datetime)]; + } + + getPreviousTransition(instant) { + if (instant.epochNanoseconds > transitionEpoch) return new Temporal.Instant(transitionEpoch); + return null; + } + + getNextTransition(instant) { + if (instant.epochNanoseconds < transitionEpoch) return new Temporal.Instant(transitionEpoch); + return null; + } + + toString() { + return "Custom/Date_Line"; + } + } + return new CrossDateLineTimeZone(); + }, + + /* + * observeProperty(calls, object, propertyName, value): + * + * Defines an own property @object.@propertyName with value @value, that + * will log any calls to its accessors to the array @calls. + */ + observeProperty(calls, object, propertyName, value, objectName = "") { + Object.defineProperty(object, propertyName, { + get() { + calls.push(`get ${formatPropertyName(propertyName, objectName)}`); + return value; + }, + set(v) { + calls.push(`set ${formatPropertyName(propertyName, objectName)}`); + } + }); + }, + + /* + * observeMethod(calls, object, propertyName, value): + * + * Defines an own property @object.@propertyName with value @value, that + * will log any calls of @value to the array @calls. + */ + observeMethod(calls, object, propertyName, objectName = "") { + const method = object[propertyName]; + object[propertyName] = function () { + calls.push(`call ${formatPropertyName(propertyName, objectName)}`); + return method.apply(object, arguments); + }; + }, + + /* + * Used for substituteMethod to indicate default behavior instead of a + * substituted value + */ + SUBSTITUTE_SKIP: SKIP_SYMBOL, + + /* + * substituteMethod(object, propertyName, values): + * + * Defines an own property @object.@propertyName that will, for each + * subsequent call to the method previously defined as + * @object.@propertyName: + * - Call the method, if no more values remain + * - Call the method, if the value in @values for the corresponding call + * is SUBSTITUTE_SKIP + * - Otherwise, return the corresponding value in @value + */ + substituteMethod(object, propertyName, values) { + let calls = 0; + const method = object[propertyName]; + object[propertyName] = function () { + if (calls >= values.length) { + return method.apply(object, arguments); + } else if (values[calls] === SKIP_SYMBOL) { + calls++; + return method.apply(object, arguments); + } else { + return values[calls++]; + } + }; + }, + + /* + * calendarObserver: + * A custom calendar that behaves exactly like the ISO 8601 calendar but + * tracks calls to any of its methods, and Get/Has operations on its + * properties, by appending messages to an array. This is for the purpose of + * testing order of operations that are observable from user code. + * objectName is used in the log. + */ + calendarObserver(calls, objectName, methodOverrides = {}) { + function removeExtraHasPropertyChecks(objectName, calls) { + // Inserting the tracking calendar into the return values of methods + // that we chain up into the ISO calendar for, causes extra HasProperty + // checks, which we observe. This removes them so that we don't leak + // implementation details of the helper into the test code. + assert.sameValue(calls.pop(), `has ${objectName}.yearOfWeek`); + assert.sameValue(calls.pop(), `has ${objectName}.yearMonthFromFields`); + assert.sameValue(calls.pop(), `has ${objectName}.year`); + assert.sameValue(calls.pop(), `has ${objectName}.weekOfYear`); + assert.sameValue(calls.pop(), `has ${objectName}.monthsInYear`); + assert.sameValue(calls.pop(), `has ${objectName}.monthDayFromFields`); + assert.sameValue(calls.pop(), `has ${objectName}.monthCode`); + assert.sameValue(calls.pop(), `has ${objectName}.month`); + assert.sameValue(calls.pop(), `has ${objectName}.mergeFields`); + assert.sameValue(calls.pop(), `has ${objectName}.inLeapYear`); + assert.sameValue(calls.pop(), `has ${objectName}.id`); + assert.sameValue(calls.pop(), `has ${objectName}.fields`); + assert.sameValue(calls.pop(), `has ${objectName}.daysInYear`); + assert.sameValue(calls.pop(), `has ${objectName}.daysInWeek`); + assert.sameValue(calls.pop(), `has ${objectName}.daysInMonth`); + assert.sameValue(calls.pop(), `has ${objectName}.dayOfYear`); + assert.sameValue(calls.pop(), `has ${objectName}.dayOfWeek`); + assert.sameValue(calls.pop(), `has ${objectName}.day`); + assert.sameValue(calls.pop(), `has ${objectName}.dateUntil`); + assert.sameValue(calls.pop(), `has ${objectName}.dateFromFields`); + assert.sameValue(calls.pop(), `has ${objectName}.dateAdd`); + } + + const iso8601 = new Temporal.Calendar("iso8601"); + const trackingMethods = { + dateFromFields(...args) { + calls.push(`call ${objectName}.dateFromFields`); + if ('dateFromFields' in methodOverrides) { + const value = methodOverrides.dateFromFields; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.dateFromFields(...args); + // Replace the calendar in the result with the call-tracking calendar + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + yearMonthFromFields(...args) { + calls.push(`call ${objectName}.yearMonthFromFields`); + if ('yearMonthFromFields' in methodOverrides) { + const value = methodOverrides.yearMonthFromFields; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.yearMonthFromFields(...args); + // Replace the calendar in the result with the call-tracking calendar + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + monthDayFromFields(...args) { + calls.push(`call ${objectName}.monthDayFromFields`); + if ('monthDayFromFields' in methodOverrides) { + const value = methodOverrides.monthDayFromFields; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.monthDayFromFields(...args); + // Replace the calendar in the result with the call-tracking calendar + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + dateAdd(...args) { + calls.push(`call ${objectName}.dateAdd`); + if ('dateAdd' in methodOverrides) { + const value = methodOverrides.dateAdd; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.dateAdd(...args); + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + id: "iso8601", + }; + // Automatically generate the other methods that don't need any custom code + [ + "dateUntil", + "day", + "dayOfWeek", + "dayOfYear", + "daysInMonth", + "daysInWeek", + "daysInYear", + "era", + "eraYear", + "fields", + "inLeapYear", + "mergeFields", + "month", + "monthCode", + "monthsInYear", + "toString", + "weekOfYear", + "year", + "yearOfWeek", + ].forEach((methodName) => { + trackingMethods[methodName] = function (...args) { + calls.push(`call ${formatPropertyName(methodName, objectName)}`); + if (methodName in methodOverrides) { + const value = methodOverrides[methodName]; + return typeof value === "function" ? value(...args) : value; + } + return iso8601[methodName](...args); + }; + }); + return new Proxy(trackingMethods, { + get(target, key, receiver) { + const result = Reflect.get(target, key, receiver); + calls.push(`get ${formatPropertyName(key, objectName)}`); + return result; + }, + has(target, key) { + calls.push(`has ${formatPropertyName(key, objectName)}`); + return Reflect.has(target, key); + }, + }); + }, + + /* + * A custom calendar that does not allow any of its methods to be called, for + * the purpose of asserting that a particular operation does not call into + * user code. + */ + calendarThrowEverything() { + class CalendarThrowEverything extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + toString() { + TemporalHelpers.assertUnreachable("toString should not be called"); + } + dateFromFields() { + TemporalHelpers.assertUnreachable("dateFromFields should not be called"); + } + yearMonthFromFields() { + TemporalHelpers.assertUnreachable("yearMonthFromFields should not be called"); + } + monthDayFromFields() { + TemporalHelpers.assertUnreachable("monthDayFromFields should not be called"); + } + dateAdd() { + TemporalHelpers.assertUnreachable("dateAdd should not be called"); + } + dateUntil() { + TemporalHelpers.assertUnreachable("dateUntil should not be called"); + } + era() { + TemporalHelpers.assertUnreachable("era should not be called"); + } + eraYear() { + TemporalHelpers.assertUnreachable("eraYear should not be called"); + } + year() { + TemporalHelpers.assertUnreachable("year should not be called"); + } + month() { + TemporalHelpers.assertUnreachable("month should not be called"); + } + monthCode() { + TemporalHelpers.assertUnreachable("monthCode should not be called"); + } + day() { + TemporalHelpers.assertUnreachable("day should not be called"); + } + fields() { + TemporalHelpers.assertUnreachable("fields should not be called"); + } + mergeFields() { + TemporalHelpers.assertUnreachable("mergeFields should not be called"); + } + } + + return new CalendarThrowEverything(); + }, + + /* + * oneShiftTimeZone(shiftInstant, shiftNanoseconds): + * + * In the case of a spring-forward time zone offset transition (skipped time), + * and disambiguation === 'earlier', BuiltinTimeZoneGetInstantFor subtracts a + * negative number of nanoseconds from a PlainDateTime, which should balance + * with the microseconds field. + * + * This returns an instance of a custom time zone class which skips a length + * of time equal to shiftNanoseconds (a number), at the Temporal.Instant + * shiftInstant. Before shiftInstant, it's identical to UTC, and after + * shiftInstant it's a constant-offset time zone. + * + * It provides a getPossibleInstantsForCalledWith member which is an array + * with the result of calling toString() on any PlainDateTimes passed to + * getPossibleInstantsFor(). + */ + oneShiftTimeZone(shiftInstant, shiftNanoseconds) { + class OneShiftTimeZone extends Temporal.TimeZone { + constructor(shiftInstant, shiftNanoseconds) { + super("+00:00"); + this._shiftInstant = shiftInstant; + this._epoch1 = shiftInstant.epochNanoseconds; + this._epoch2 = this._epoch1 + BigInt(shiftNanoseconds); + this._shiftNanoseconds = shiftNanoseconds; + this._shift = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, this._shiftNanoseconds); + this.getPossibleInstantsForCalledWith = []; + } + + _isBeforeShift(instant) { + return instant.epochNanoseconds < this._epoch1; + } + + getOffsetNanosecondsFor(instant) { + return this._isBeforeShift(instant) ? 0 : this._shiftNanoseconds; + } + + getPossibleInstantsFor(plainDateTime) { + this.getPossibleInstantsForCalledWith.push(plainDateTime.toString({ calendarName: "never" })); + const [instant] = super.getPossibleInstantsFor(plainDateTime); + if (this._shiftNanoseconds > 0) { + if (this._isBeforeShift(instant)) return [instant]; + if (instant.epochNanoseconds < this._epoch2) return []; + return [instant.subtract(this._shift)]; + } + if (instant.epochNanoseconds < this._epoch2) return [instant]; + const shifted = instant.subtract(this._shift); + if (this._isBeforeShift(instant)) return [instant, shifted]; + return [shifted]; + } + + getNextTransition(instant) { + return this._isBeforeShift(instant) ? this._shiftInstant : null; + } + + getPreviousTransition(instant) { + return this._isBeforeShift(instant) ? null : this._shiftInstant; + } + + toString() { + return "Custom/One_Shift"; + } + } + return new OneShiftTimeZone(shiftInstant, shiftNanoseconds); + }, + + /* + * propertyBagObserver(): + * Returns an object that behaves like the given propertyBag but tracks Get + * and Has operations on any of its properties, by appending messages to an + * array. If the value of a property in propertyBag is a primitive, the value + * of the returned object's property will additionally be a + * TemporalHelpers.toPrimitiveObserver that will track calls to its toString + * and valueOf methods in the same array. This is for the purpose of testing + * order of operations that are observable from user code. objectName is used + * in the log. + */ + propertyBagObserver(calls, propertyBag, objectName) { + return new Proxy(propertyBag, { + ownKeys(target) { + calls.push(`ownKeys ${objectName}`); + return Reflect.ownKeys(target); + }, + getOwnPropertyDescriptor(target, key) { + calls.push(`getOwnPropertyDescriptor ${formatPropertyName(key, objectName)}`); + return Reflect.getOwnPropertyDescriptor(target, key); + }, + get(target, key, receiver) { + calls.push(`get ${formatPropertyName(key, objectName)}`); + const result = Reflect.get(target, key, receiver); + if (result === undefined) { + return undefined; + } + if ((result !== null && typeof result === "object") || typeof result === "function") { + return result; + } + return TemporalHelpers.toPrimitiveObserver(calls, result, `${formatPropertyName(key, objectName)}`); + }, + has(target, key) { + calls.push(`has ${formatPropertyName(key, objectName)}`); + return Reflect.has(target, key); + }, + }); + }, + + /* + * specificOffsetTimeZone(): + * + * This returns an instance of a custom time zone class, which returns a + * specific custom value from its getOffsetNanosecondsFrom() method. This is + * for the purpose of testing the validation of what this method returns. + * + * It also returns an empty array from getPossibleInstantsFor(), so as to + * trigger calls to getOffsetNanosecondsFor() when used from the + * BuiltinTimeZoneGetInstantFor operation. + */ + specificOffsetTimeZone(offsetValue) { + class SpecificOffsetTimeZone extends Temporal.TimeZone { + constructor(offsetValue) { + super("UTC"); + this._offsetValue = offsetValue; + } + + getOffsetNanosecondsFor() { + return this._offsetValue; + } + + getPossibleInstantsFor(dt) { + if (typeof this._offsetValue !== 'number' || Math.abs(this._offsetValue) >= 86400e9 || isNaN(this._offsetValue)) return []; + const zdt = dt.toZonedDateTime("UTC").add({ nanoseconds: -this._offsetValue }); + return [zdt.toInstant()]; + } + + get id() { + return this.getOffsetStringFor(new Temporal.Instant(0n)); + } + } + return new SpecificOffsetTimeZone(offsetValue); + }, + + /* + * springForwardFallBackTimeZone(): + * + * This returns an instance of a custom time zone class that implements one + * single spring-forward/fall-back transition, for the purpose of testing the + * disambiguation option, without depending on system time zone data. + * + * The spring-forward occurs at epoch second 954669600 (2000-04-02T02:00 + * local) and goes from offset -08:00 to -07:00. + * + * The fall-back occurs at epoch second 972810000 (2000-10-29T02:00 local) and + * goes from offset -07:00 to -08:00. + */ + springForwardFallBackTimeZone() { + const { compare } = Temporal.PlainDateTime; + const springForwardLocal = new Temporal.PlainDateTime(2000, 4, 2, 2); + const springForwardEpoch = 954669600_000_000_000n; + const fallBackLocal = new Temporal.PlainDateTime(2000, 10, 29, 1); + const fallBackEpoch = 972810000_000_000_000n; + const winterOffset = new Temporal.TimeZone('-08:00'); + const summerOffset = new Temporal.TimeZone('-07:00'); + + class SpringForwardFallBackTimeZone extends Temporal.TimeZone { + constructor() { + super("-08:00"); + } + + getOffsetNanosecondsFor(instant) { + if (instant.epochNanoseconds < springForwardEpoch || + instant.epochNanoseconds >= fallBackEpoch) { + return winterOffset.getOffsetNanosecondsFor(instant); + } + return summerOffset.getOffsetNanosecondsFor(instant); + } + + getPossibleInstantsFor(datetime) { + if (compare(datetime, springForwardLocal) >= 0 && compare(datetime, springForwardLocal.add({ hours: 1 })) < 0) { + return []; + } + if (compare(datetime, fallBackLocal) >= 0 && compare(datetime, fallBackLocal.add({ hours: 1 })) < 0) { + return [summerOffset.getInstantFor(datetime), winterOffset.getInstantFor(datetime)]; + } + if (compare(datetime, springForwardLocal) < 0 || compare(datetime, fallBackLocal) >= 0) { + return [winterOffset.getInstantFor(datetime)]; + } + return [summerOffset.getInstantFor(datetime)]; + } + + getPreviousTransition(instant) { + if (instant.epochNanoseconds > fallBackEpoch) return new Temporal.Instant(fallBackEpoch); + if (instant.epochNanoseconds > springForwardEpoch) return new Temporal.Instant(springForwardEpoch); + return null; + } + + getNextTransition(instant) { + if (instant.epochNanoseconds < springForwardEpoch) return new Temporal.Instant(springForwardEpoch); + if (instant.epochNanoseconds < fallBackEpoch) return new Temporal.Instant(fallBackEpoch); + return null; + } + + get id() { + return "Custom/Spring_Fall"; + } + + toString() { + return "Custom/Spring_Fall"; + } + } + return new SpringForwardFallBackTimeZone(); + }, + + /* + * timeZoneObserver: + * A custom calendar that behaves exactly like the UTC time zone but tracks + * calls to any of its methods, and Get/Has operations on its properties, by + * appending messages to an array. This is for the purpose of testing order of + * operations that are observable from user code. objectName is used in the + * log. methodOverrides is an optional object containing properties with the + * same name as Temporal.TimeZone methods. If the property value is a function + * it will be called with the proper arguments instead of the UTC method. + * Otherwise, the property value will be returned directly. + */ + timeZoneObserver(calls, objectName, methodOverrides = {}) { + const utc = new Temporal.TimeZone("UTC"); + const trackingMethods = { + id: "UTC", + }; + // Automatically generate the methods + ["getOffsetNanosecondsFor", "getPossibleInstantsFor", "toString"].forEach((methodName) => { + trackingMethods[methodName] = function (...args) { + calls.push(`call ${formatPropertyName(methodName, objectName)}`); + if (methodName in methodOverrides) { + const value = methodOverrides[methodName]; + return typeof value === "function" ? value(...args) : value; + } + return utc[methodName](...args); + }; + }); + return new Proxy(trackingMethods, { + get(target, key, receiver) { + const result = Reflect.get(target, key, receiver); + calls.push(`get ${formatPropertyName(key, objectName)}`); + return result; + }, + has(target, key) { + calls.push(`has ${formatPropertyName(key, objectName)}`); + return Reflect.has(target, key); + }, + }); + }, + + /* + * A custom time zone that does not allow any of its methods to be called, for + * the purpose of asserting that a particular operation does not call into + * user code. + */ + timeZoneThrowEverything() { + class TimeZoneThrowEverything extends Temporal.TimeZone { + constructor() { + super("UTC"); + } + getOffsetNanosecondsFor() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be called"); + } + getPossibleInstantsFor() { + TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be called"); + } + toString() { + TemporalHelpers.assertUnreachable("toString should not be called"); + } + } + + return new TimeZoneThrowEverything(); + }, + + /* + * Returns an object that will append logs of any Gets or Calls of its valueOf + * or toString properties to the array calls. Both valueOf and toString will + * return the actual primitiveValue. propertyName is used in the log. + */ + toPrimitiveObserver(calls, primitiveValue, propertyName) { + return { + get valueOf() { + calls.push(`get ${propertyName}.valueOf`); + return function () { + calls.push(`call ${propertyName}.valueOf`); + return primitiveValue; + }; + }, + get toString() { + calls.push(`get ${propertyName}.toString`); + return function () { + calls.push(`call ${propertyName}.toString`); + if (primitiveValue === undefined) return undefined; + return primitiveValue.toString(); + }; + }, + }; + }, + + /* + * An object containing further methods that return arrays of ISO strings, for + * testing parsers. + */ + ISO: { + /* + * PlainMonthDay strings that are not valid. + */ + plainMonthDayStringsInvalid() { + return [ + "11-18junk", + "11-18[u-ca=gregory]", + "11-18[u-ca=hebrew]", + ]; + }, + + /* + * PlainMonthDay strings that are valid and that should produce October 1st. + */ + plainMonthDayStringsValid() { + return [ + "10-01", + "1001", + "1965-10-01", + "1976-10-01T152330.1+00:00", + "19761001T15:23:30.1+00:00", + "1976-10-01T15:23:30.1+0000", + "1976-10-01T152330.1+0000", + "19761001T15:23:30.1+0000", + "19761001T152330.1+00:00", + "19761001T152330.1+0000", + "+001976-10-01T152330.1+00:00", + "+0019761001T15:23:30.1+00:00", + "+001976-10-01T15:23:30.1+0000", + "+001976-10-01T152330.1+0000", + "+0019761001T15:23:30.1+0000", + "+0019761001T152330.1+00:00", + "+0019761001T152330.1+0000", + "1976-10-01T15:23:00", + "1976-10-01T15:23", + "1976-10-01T15", + "1976-10-01", + "--10-01", + "--1001", + ]; + }, + + /* + * PlainTime strings that may be mistaken for PlainMonthDay or + * PlainYearMonth strings, and so require a time designator. + */ + plainTimeStringsAmbiguous() { + const ambiguousStrings = [ + "2021-12", // ambiguity between YYYY-MM and HHMM-UU + "2021-12[-12:00]", // ditto, TZ does not disambiguate + "1214", // ambiguity between MMDD and HHMM + "0229", // ditto, including MMDD that doesn't occur every year + "1130", // ditto, including DD that doesn't occur in every month + "12-14", // ambiguity between MM-DD and HH-UU + "12-14[-14:00]", // ditto, TZ does not disambiguate + "202112", // ambiguity between YYYYMM and HHMMSS + "202112[UTC]", // ditto, TZ does not disambiguate + ]; + // Adding a calendar annotation to one of these strings must not cause + // disambiguation in favour of time. + const stringsWithCalendar = ambiguousStrings.map((s) => s + '[u-ca=iso8601]'); + return ambiguousStrings.concat(stringsWithCalendar); + }, + + /* + * PlainTime strings that are of similar form to PlainMonthDay and + * PlainYearMonth strings, but are not ambiguous due to components that + * aren't valid as months or days. + */ + plainTimeStringsUnambiguous() { + return [ + "2021-13", // 13 is not a month + "202113", // ditto + "2021-13[-13:00]", // ditto + "202113[-13:00]", // ditto + "0000-00", // 0 is not a month + "000000", // ditto + "0000-00[UTC]", // ditto + "000000[UTC]", // ditto + "1314", // 13 is not a month + "13-14", // ditto + "1232", // 32 is not a day + "0230", // 30 is not a day in February + "0631", // 31 is not a day in June + "0000", // 0 is neither a month nor a day + "00-00", // ditto + ]; + }, + + /* + * PlainYearMonth-like strings that are not valid. + */ + plainYearMonthStringsInvalid() { + return [ + "2020-13", + ]; + }, + + /* + * PlainYearMonth-like strings that are valid and should produce November + * 1976 in the ISO 8601 calendar. + */ + plainYearMonthStringsValid() { + return [ + "1976-11", + "1976-11-10", + "1976-11-01T09:00:00+00:00", + "1976-11-01T00:00:00+05:00", + "197611", + "+00197611", + "1976-11-18T15:23:30.1\u221202:00", + "1976-11-18T152330.1+00:00", + "19761118T15:23:30.1+00:00", + "1976-11-18T15:23:30.1+0000", + "1976-11-18T152330.1+0000", + "19761118T15:23:30.1+0000", + "19761118T152330.1+00:00", + "19761118T152330.1+0000", + "+001976-11-18T152330.1+00:00", + "+0019761118T15:23:30.1+00:00", + "+001976-11-18T15:23:30.1+0000", + "+001976-11-18T152330.1+0000", + "+0019761118T15:23:30.1+0000", + "+0019761118T152330.1+00:00", + "+0019761118T152330.1+0000", + "1976-11-18T15:23", + "1976-11-18T15", + "1976-11-18", + ]; + }, + + /* + * PlainYearMonth-like strings that are valid and should produce November of + * the ISO year -9999. + */ + plainYearMonthStringsValidNegativeYear() { + return [ + "\u2212009999-11", + ]; + }, + } +}; diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/timezone-offset.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/timezone-offset.js new file mode 100644 index 0000000000..422507917a --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/timezone-offset.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-timezone-objects +description: +01:00 +features: [Temporal] +---*/ + +var zone = new Temporal.TimeZone("+01:00"); +var inst = Temporal.Instant.fromEpochSeconds(Math.floor(Math.random() * 1000000000)); +var dtm = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789); +assert.sameValue(zone.id, `${ zone }`) +assert.sameValue(zone.getOffsetNanosecondsFor(inst), 3600000000000) +assert(zone.getPlainDateTimeFor(inst) instanceof Temporal.PlainDateTime) +assert(zone.getInstantFor(dtm) instanceof Temporal.Instant) +assert.sameValue(zone.getNextTransition(inst), null) +assert.sameValue(zone.getPreviousTransition(inst), null) + +// wraps around to the next day +assert.sameValue(`${ zone.getPlainDateTimeFor(Temporal.Instant.from("2020-02-06T23:59Z")) }`, "2020-02-07T00:59:00") + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/timezone-utc-offset.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/timezone-utc-offset.js new file mode 100644 index 0000000000..7d8a75e581 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/timezone-utc-offset.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-timezone-objects +description: UTC +features: [Temporal] +---*/ + +var zone = new Temporal.TimeZone("UTC"); +var inst = Temporal.Instant.fromEpochSeconds(Math.floor(Math.random() * 1000000000)); +var dtm = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789); +assert.sameValue(zone.id, `${ zone }`) +assert.sameValue(zone.getOffsetNanosecondsFor(inst), 0) +assert(zone.getPlainDateTimeFor(inst) instanceof Temporal.PlainDateTime) +assert(zone.getInstantFor(dtm) instanceof Temporal.Instant) +assert.sameValue(zone.getNextTransition(inst), null) +assert.sameValue(zone.getPreviousTransition(inst), null) + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/shell.js b/js/src/tests/test262/staging/Temporal/TimeZone/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/shell.js diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/browser.js b/js/src/tests/test262/staging/Temporal/UserCalendar/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserCalendar/browser.js diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/old/browser.js b/js/src/tests/test262/staging/Temporal/UserCalendar/old/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserCalendar/old/browser.js diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/old/calendar-extra-fields.js b/js/src/tests/test262/staging/Temporal/UserCalendar/old/calendar-extra-fields.js new file mode 100644 index 0000000000..b9b3c0bc56 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserCalendar/old/calendar-extra-fields.js @@ -0,0 +1,160 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: calendar with extra fields +features: [Temporal, Array.prototype.includes] +---*/ + +class SeasonCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + get id() { + return "season"; + } + toString() { + return "season"; + } + month(date) { + var {isoMonth} = date.getISOFields(); + return (isoMonth - 1) % 3 + 1; + } + monthCode(date) { + return `M${ this.month(date).toString().padStart(2, "0") }`; + } + season(date) { + var {isoMonth} = date.getISOFields(); + return Math.floor((isoMonth - 1) / 3) + 1; + } + _isoMonthCode(fields) { + var month = fields.month || +fields.monthCode.slice(1); + return `M${ ((fields.season - 1) * 3 + month).toString().padStart(2, "0") }`; + } + dateFromFields(fields, options) { + var monthCode = this._isoMonthCode(fields); + delete fields.month; + return super.dateFromFields({ + ...fields, + monthCode + }, options).withCalendar(this); + } + yearMonthFromFields(fields, options) { + var monthCode = this._isoMonthCode(fields); + delete fields.month; + const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields({ + ...fields, + monthCode + }, options).getISOFields(); + return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); + } + monthDayFromFields(fields, options) { + var monthCode = this._isoMonthCode(fields); + delete fields.month; + const { isoYear, isoMonth, isoDay } = super.monthDayFromFields({ + ...fields, + monthCode + }, options).getISOFields(); + return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); + } + fields(fields) { + fields = fields.slice(); + if (fields.includes("month") || fields.includes("monthCode")) + fields.push("season"); + return fields; + } +} +var calendar = new SeasonCalendar(); +var datetime = new Temporal.PlainDateTime(2019, 9, 15, 0, 0, 0, 0, 0, 0, calendar); +var date = new Temporal.PlainDate(2019, 9, 15, calendar); +var yearmonth = new Temporal.PlainYearMonth(2019, 9, calendar); +var monthday = new Temporal.PlainMonthDay(9, 15, calendar); +var zoned = new Temporal.ZonedDateTime(1568505600000000000n, "UTC", calendar); +var propDesc = { + get() { + return this.getCalendar().season(this); + }, + configurable: true +}; +Object.defineProperty(Temporal.PlainDateTime.prototype, "season", propDesc); +Object.defineProperty(Temporal.PlainDate.prototype, "season", propDesc); +Object.defineProperty(Temporal.PlainYearMonth.prototype, "season", propDesc); +Object.defineProperty(Temporal.PlainMonthDay.prototype, "season", propDesc); +Object.defineProperty(Temporal.ZonedDateTime.prototype, "season", propDesc); + +// property getter works +assert.sameValue(datetime.season, 3); +assert.sameValue(datetime.month, 3); +assert.sameValue(datetime.monthCode, "M03"); +assert.sameValue(date.season, 3); +assert.sameValue(date.month, 3); +assert.sameValue(date.monthCode, "M03"); +assert.sameValue(yearmonth.season, 3); +assert.sameValue(yearmonth.month, 3); +assert.sameValue(yearmonth.monthCode, "M03"); +assert.sameValue(monthday.season, 3); +assert.sameValue(monthday.monthCode, "M03"); +assert.sameValue(zoned.season, 3); +assert.sameValue(zoned.month, 3); +assert.sameValue(zoned.monthCode, "M03"); + +// accepts season in from() +assert.sameValue(`${ Temporal.PlainDateTime.from({ + year: 2019, + season: 3, + month: 3, + day: 15, + calendar +}) }`, "2019-09-15T00:00:00[u-ca=season]"); +assert.sameValue(`${ Temporal.PlainDate.from({ + year: 2019, + season: 3, + month: 3, + day: 15, + calendar +}) }`, "2019-09-15[u-ca=season]"); +assert.sameValue(`${ Temporal.PlainYearMonth.from({ + year: 2019, + season: 3, + month: 3, + calendar +}) }`, "2019-09-01[u-ca=season]"); +assert.sameValue(`${ Temporal.PlainMonthDay.from({ + season: 3, + monthCode: "M03", + day: 15, + calendar +}) }`, "1972-09-15[u-ca=season]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from({ + year: 2019, + season: 3, + month: 3, + day: 15, + timeZone: "UTC", + calendar +}) }`, "2019-09-15T00:00:00+00:00[UTC][u-ca=season]"); + +// accepts season in with() +assert.sameValue(`${ datetime.with({ season: 2 }) }`, "2019-06-15T00:00:00[u-ca=season]"); +assert.sameValue(`${ date.with({ season: 2 }) }`, "2019-06-15[u-ca=season]"); +assert.sameValue(`${ yearmonth.with({ season: 2 }) }`, "2019-06-01[u-ca=season]"); +assert.sameValue(`${ monthday.with({ season: 2 }) }`, "1972-06-15[u-ca=season]"); +assert.sameValue(`${ zoned.with({ season: 2 }) }`, "2019-06-15T00:00:00+00:00[UTC][u-ca=season]"); + +// translates month correctly in with() +assert.sameValue(`${ datetime.with({ month: 2 }) }`, "2019-08-15T00:00:00[u-ca=season]"); +assert.sameValue(`${ date.with({ month: 2 }) }`, "2019-08-15[u-ca=season]"); +assert.sameValue(`${ yearmonth.with({ month: 2 }) }`, "2019-08-01[u-ca=season]"); +assert.sameValue(`${ monthday.with({ monthCode: "M02" }) }`, "1972-08-15[u-ca=season]"); +assert.sameValue(`${ zoned.with({ month: 2 }) }`, "2019-08-15T00:00:00+00:00[UTC][u-ca=season]"); + +delete Temporal.PlainDateTime.prototype.season; +delete Temporal.PlainDate.prototype.season; +delete Temporal.PlainYearMonth.prototype.season; +delete Temporal.PlainMonthDay.prototype.season; +delete Temporal.ZonedDateTime.prototype.season; + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/old/calendar-non-trivial-mergefields.js b/js/src/tests/test262/staging/Temporal/UserCalendar/old/calendar-non-trivial-mergefields.js new file mode 100644 index 0000000000..a3a836525f --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserCalendar/old/calendar-non-trivial-mergefields.js @@ -0,0 +1,154 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: calendar with nontrivial mergeFields implementation +features: [Temporal, Array.prototype.includes] +---*/ + +class CenturyCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + get id() { + return 'century'; + } + toString() { + return "century"; + } + century(date) { + var {isoYear} = date.getISOFields(); + return Math.ceil(isoYear / 100); + } + centuryYear(date) { + var {isoYear} = date.getISOFields(); + return isoYear % 100; + } + _validateFields(fields) { + var {year, century, centuryYear} = fields; + if (century === undefined !== (centuryYear === undefined)) { + throw new TypeError("pass either both or neither of century and centuryYear"); + } + if (year === undefined) + return (century - 1) * 100 + centuryYear; + if (century !== undefined) { + var centuryCalculatedYear = (century - 1) * 100 + centuryYear; + if (year !== centuryCalculatedYear) { + throw new RangeError("year must agree with century/centuryYear if both given"); + } + } + return year; + } + dateFromFields(fields, options) { + var isoYear = this._validateFields(fields); + return super.dateFromFields({ + ...fields, + year: isoYear + }, options).withCalendar(this); + } + yearMonthFromFields(fields, options) { + var year = this._validateFields(fields); + const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields({ + ...fields, + year, + }, options).getISOFields(); + return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); + } + monthDayFromFields(fields, options) { + var year = this._validateFields(fields); + const { isoYear, isoMonth, isoDay } = super.monthDayFromFields({ + ...fields, + year, + }, options).getISOFields(); + return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); + } + fields(fields) { + fields = fields.slice(); + if (fields.includes("year")) + fields.push("century", "centuryYear"); + return fields; + } + mergeFields(fields, additionalFields) { + var {year, century, centuryYear, ...original} = fields; + var { + year: newYear, + century: newCentury, + centuryYear: newCenturyYear + } = additionalFields; + if (newYear === undefined) { + original.century = century; + original.centuryYear = centuryYear; + } + if (newCentury === undefined && newCenturyYear === undefined) { + original.year === year; + } + return { + ...original, + ...additionalFields + }; + } +} +var calendar = new CenturyCalendar(); +var datetime = new Temporal.PlainDateTime(2019, 9, 15, 0, 0, 0, 0, 0, 0, calendar); +var date = new Temporal.PlainDate(2019, 9, 15, calendar); +var yearmonth = new Temporal.PlainYearMonth(2019, 9, calendar); +var zoned = new Temporal.ZonedDateTime(1568505600000000000n, "UTC", calendar); +var propDesc = { + century: { + get() { + return this.getCalendar().century(this); + }, + configurable: true + }, + centuryYear: { + get() { + return this.getCalendar().centuryYear(this); + }, + configurable: true + } +}; +Object.defineProperties(Temporal.PlainDateTime.prototype, propDesc); +Object.defineProperties(Temporal.PlainDate.prototype, propDesc); +Object.defineProperties(Temporal.PlainYearMonth.prototype, propDesc); +Object.defineProperties(Temporal.ZonedDateTime.prototype, propDesc); + +// property getters work +assert.sameValue(datetime.century, 21); +assert.sameValue(datetime.centuryYear, 19); +assert.sameValue(date.century, 21); +assert.sameValue(date.centuryYear, 19); +assert.sameValue(yearmonth.century, 21); +assert.sameValue(yearmonth.centuryYear, 19); +assert.sameValue(zoned.century, 21); +assert.sameValue(zoned.centuryYear, 19); + +// correctly resolves century in with() +assert.sameValue(`${ datetime.with({ century: 20 }) }`, "1919-09-15T00:00:00[u-ca=century]"); +assert.sameValue(`${ date.with({ century: 20 }) }`, "1919-09-15[u-ca=century]"); +assert.sameValue(`${ yearmonth.with({ century: 20 }) }`, "1919-09-01[u-ca=century]"); +assert.sameValue(`${ zoned.with({ century: 20 }) }`, "1919-09-15T00:00:00+00:00[UTC][u-ca=century]"); + +// correctly resolves centuryYear in with() +assert.sameValue(`${ datetime.with({ centuryYear: 5 }) }`, "2005-09-15T00:00:00[u-ca=century]"); +assert.sameValue(`${ date.with({ centuryYear: 5 }) }`, "2005-09-15[u-ca=century]"); +assert.sameValue(`${ yearmonth.with({ centuryYear: 5 }) }`, "2005-09-01[u-ca=century]"); +assert.sameValue(`${ zoned.with({ centuryYear: 5 }) }`, "2005-09-15T00:00:00+00:00[UTC][u-ca=century]"); + +// correctly resolves year in with() +assert.sameValue(`${ datetime.with({ year: 1974 }) }`, "1974-09-15T00:00:00[u-ca=century]"); +assert.sameValue(`${ date.with({ year: 1974 }) }`, "1974-09-15[u-ca=century]"); +assert.sameValue(`${ yearmonth.with({ year: 1974 }) }`, "1974-09-01[u-ca=century]"); +assert.sameValue(`${ zoned.with({ year: 1974 }) }`, "1974-09-15T00:00:00+00:00[UTC][u-ca=century]"); + +delete Temporal.PlainDateTime.prototype.century; +delete Temporal.PlainDateTime.prototype.centuryYear; +delete Temporal.PlainDate.prototype.century; +delete Temporal.PlainDate.prototype.centuryYear; +delete Temporal.PlainYearMonth.prototype.century; +delete Temporal.PlainYearMonth.prototype.centuryYear; +delete Temporal.ZonedDateTime.prototype.century; +delete Temporal.ZonedDateTime.prototype.centuryYear; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/old/shell.js b/js/src/tests/test262/staging/Temporal/UserCalendar/old/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserCalendar/old/shell.js diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/old/trivial-protocol-implementation.js b/js/src/tests/test262/staging/Temporal/UserCalendar/old/trivial-protocol-implementation.js new file mode 100644 index 0000000000..b3bfd43e09 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserCalendar/old/trivial-protocol-implementation.js @@ -0,0 +1,231 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Trivial protocol implementation +features: [Temporal] +---*/ + +function decimalToISO(year, month, day, overflow = "constrain") { + if (overflow === "constrain") { + if (month < 1) + month = 1; + if (month > 10) + month = 10; + if (day < 1) + day = 1; + if (day > 10) + day = 10; + } else if (overflow === "reject") { + if (month < 1 || month > 10 || day < 1 || day > 10) { + throw new RangeError("invalid value"); + } + } + var days = year * 100 + (month - 1) * 10 + (day - 1); + return new Temporal.PlainDate(1970, 1, 1, "iso8601").add({ days }); +} +function isoToDecimal(date) { + var {isoYear, isoMonth, isoDay} = date.getISOFields(); + var isoDate = new Temporal.PlainDate(isoYear, isoMonth, isoDay); + var {days} = isoDate.since(new Temporal.PlainDate(1970, 1, 1), { largestUnit: "days" }); + var year = Math.floor(days / 100); + days %= 100; + return { + year, + days + }; +} +var obj = { + id: 'decimal', + toString() { + return "decimal"; + }, + fields(fieldNames) { return fieldNames; }, + dateFromFields(fields, options) { + var { + overflow = "constrain" + } = options ? options : {}; + var {month, monthCode} = fields; + if (month === undefined) + month = +monthCode.slice(1); + var isoDate = decimalToISO(fields.year, month, fields.day, 0, 0, 0, overflow); + return new Temporal.PlainDate(isoDate.year, isoDate.month, isoDate.day, this); + }, + yearMonthFromFields(fields, options) { + var { + overflow = "constrain" + } = options ? options : {}; + var {month, monthCode} = fields; + if (month === undefined) + month = +monthCode.slice(1); + var isoDate = decimalToISO(fields.year, month, 1, 0, 0, 0, overflow); + return new Temporal.PlainYearMonth(isoDate.year, isoDate.month, this, isoDate.day); + }, + monthDayFromFields(fields, options) { + var { + overflow = "constrain" + } = options ? options : {}; + var {month, monthCode} = fields; + if (month === undefined) + month = +monthCode.slice(1); + var isoDate = decimalToISO(0, month, fields.day, 0, 0, 0, overflow); + return new Temporal.PlainMonthDay(isoDate.month, isoDate.day, this, isoDate.year); + }, + year(date) { + return isoToDecimal(date).year; + }, + month(date) { + var {days} = isoToDecimal(date); + return Math.floor(days / 10) + 1; + }, + monthCode(date) { + return `M${ this.month(date).toString().padStart(2, "0") }`; + }, + day(date) { + var {days} = isoToDecimal(date); + return days % 10 + 1; + }, + dateAdd() {}, // left as an exercise for the reader + dateUntil() {}, // ditto + dayOfWeek() { + throw new Error('no weeks'); + }, + dayOfYear(date) { + return isoToDecimal(date).days; + }, + daysInMonth() { + return 10; + }, + daysInWeek() { + throw new Error('no weeks'); + }, + daysInYear() { + return 100; + }, + fields(fields) { + return fields; + }, + inLeapYear() { + return false; + }, + mergeFields(fields, additional) { + return new Temporal.Calendar("iso8601").mergeFields(fields, additional) + }, + monthsInYear() { + return 10; + }, + weekOfYear() { + throw new Error('no weeks'); + }, + yearOfWeek(date) { + throw new Error('no weeks'); + }, +}; +var date = Temporal.PlainDate.from({ + year: 184, + month: 2, + day: 9, + calendar: obj +}); +var dt = Temporal.PlainDateTime.from({ + year: 184, + month: 2, + day: 9, + hour: 12, + calendar: obj +}); +var ym = Temporal.PlainYearMonth.from({ + year: 184, + month: 2, + calendar: obj +}); +var md = Temporal.PlainMonthDay.from({ + monthCode: "M02", + day: 9, + calendar: obj +}); + +// is a calendar +assert.sameValue(typeof obj, "object") + +// .id is not available in from() +assert.throws(RangeError, () => Temporal.Calendar.from("decimal")); +assert.throws(RangeError, () => Temporal.Calendar.from("2020-06-05T09:34-00:00[UTC][u-ca=decimal]")); + +// Temporal.PlainDate.from() +assert.sameValue(`${ date }`, "2020-06-05[u-ca=decimal]") + +// Temporal.PlainDate fields +assert.sameValue(date.year, 184); +assert.sameValue(date.month, 2); +assert.sameValue(date.day, 9); + +// date.with() +var date2 = date.with({ year: 0 }); +assert.sameValue(date2.year, 0); + +// date.withCalendar() +var date2 = Temporal.PlainDate.from("2020-06-05T12:00"); +assert(date2.withCalendar(obj).equals(date)); + +// Temporal.PlainDateTime.from() +assert.sameValue(`${ dt }`, "2020-06-05T12:00:00[u-ca=decimal]") + +// Temporal.PlainDateTime fields +assert.sameValue(dt.year, 184); +assert.sameValue(dt.month, 2); +assert.sameValue(dt.day, 9); +assert.sameValue(dt.hour, 12); +assert.sameValue(dt.minute, 0); +assert.sameValue(dt.second, 0); +assert.sameValue(dt.millisecond, 0); +assert.sameValue(dt.microsecond, 0); +assert.sameValue(dt.nanosecond, 0); + +// datetime.with() +var dt2 = dt.with({ year: 0 }); +assert.sameValue(dt2.year, 0); + +// datetime.withCalendar() +var dt2 = Temporal.PlainDateTime.from("2020-06-05T12:00"); +assert(dt2.withCalendar(obj).equals(dt)); + +// Temporal.PlainYearMonth.from() +assert.sameValue(`${ ym }`, "2020-05-28[u-ca=decimal]") + +// Temporal.PlainYearMonth fields +assert.sameValue(dt.year, 184); +assert.sameValue(dt.month, 2); + +// yearmonth.with() +var ym2 = ym.with({ year: 0 }); +assert.sameValue(ym2.year, 0); + +// Temporal.PlainMonthDay.from() +assert.sameValue(`${ md }`, "1970-01-19[u-ca=decimal]") + +// Temporal.PlainMonthDay fields +assert.sameValue(md.monthCode, "M02"); +assert.sameValue(md.day, 9); + +// monthday.with() +var md2 = md.with({ monthCode: "M01" }); +assert.sameValue(md2.monthCode, "M01"); + +// timezone.getPlainDateTimeFor() +var tz = Temporal.TimeZone.from("UTC"); +var inst = Temporal.Instant.fromEpochSeconds(0); +var dt = tz.getPlainDateTimeFor(inst, obj); +assert.sameValue(dt.getCalendar(), obj); + +// Temporal.Now.plainDateTime() +var nowDateTime = Temporal.Now.plainDateTime(obj, "UTC"); +assert.sameValue(nowDateTime.getCalendar(), obj); + +// Temporal.Now.plainDate() +var nowDate = Temporal.Now.plainDate(obj, "UTC"); +assert.sameValue(nowDate.getCalendar(), obj); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/old/trivial-subclass.js b/js/src/tests/test262/staging/Temporal/UserCalendar/old/trivial-subclass.js new file mode 100644 index 0000000000..b96e75c95c --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserCalendar/old/trivial-subclass.js @@ -0,0 +1,167 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Trivial subclass +features: [Temporal] +---*/ + +class TwoBasedCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + get id() { + return "two-based"; + } + toString() { + return "two-based"; + } + dateFromFields(fields, options) { + var {year, month, monthCode, day} = fields; + if (month === undefined) + month = +monthCode.slice(1); + return super.dateFromFields({ + year, + monthCode: `M${ (month - 1).toString().padStart(2, "0") }`, + day + }, options).withCalendar(this); + } + yearMonthFromFields(fields, options) { + var {year, month, monthCode} = fields; + if (month === undefined) + month = +monthCode.slice(1); + const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields({ + year, + monthCode: `M${ (month - 1).toString().padStart(2, "0") }` + }, options).getISOFields(); + return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); + } + monthDayFromFields(fields, options) { + var {month, monthCode, day} = fields; + if (month === undefined) + month = +monthCode.slice(1); + const { isoYear, isoMonth, isoDay } = super.monthDayFromFields({ + monthCode: `M${ (month - 1).toString().padStart(2, "0") }`, + day + }, options).getISOFields(); + return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); + } + month(date) { + return date.getISOFields().isoMonth + 1; + } + monthCode(date) { + return `M${ this.month(date).toString().padStart(2, "0") }`; + } +} +var obj = new TwoBasedCalendar(); +var date = Temporal.PlainDate.from({ + year: 2020, + month: 5, + day: 5, + calendar: obj +}); +var dt = Temporal.PlainDateTime.from({ + year: 2020, + month: 5, + day: 5, + hour: 12, + calendar: obj +}); +var ym = Temporal.PlainYearMonth.from({ + year: 2020, + month: 5, + calendar: obj +}); +var md = Temporal.PlainMonthDay.from({ + monthCode: "M05", + day: 5, + calendar: obj +}); + +// is a calendar +assert.sameValue(typeof obj, "object") + +// .id property +assert.sameValue(obj.id, "two-based") + +// .id is not available in from() +assert.throws(RangeError, () => Temporal.Calendar.from("two-based")); +assert.throws(RangeError, () => Temporal.Calendar.from("2020-06-05T09:34-00:00[UTC][u-ca=two-based]")); + +// Temporal.PlainDate.from() +assert.sameValue(`${ date }`, "2020-04-05[u-ca=two-based]") + +// Temporal.PlainDate fields +assert.sameValue(date.year, 2020); +assert.sameValue(date.month, 5); +assert.sameValue(date.day, 5); + +// date.with() +var date2 = date.with({ month: 2 }); +assert.sameValue(date2.month, 2); + +// date.withCalendar() +var date2 = Temporal.PlainDate.from("2020-04-05"); +assert(date2.withCalendar(obj).equals(date)); + +// Temporal.PlainDateTime.from() +assert.sameValue(`${ dt }`, "2020-04-05T12:00:00[u-ca=two-based]") + +// Temporal.PlainDateTime fields +assert.sameValue(dt.year, 2020); +assert.sameValue(dt.month, 5); +assert.sameValue(dt.day, 5); +assert.sameValue(dt.hour, 12); +assert.sameValue(dt.minute, 0); +assert.sameValue(dt.second, 0); +assert.sameValue(dt.millisecond, 0); +assert.sameValue(dt.microsecond, 0); +assert.sameValue(dt.nanosecond, 0); + +// datetime.with() +var dt2 = dt.with({ month: 2 }); +assert.sameValue(dt2.month, 2); + +// datetime.withCalendar() +var dt2 = Temporal.PlainDateTime.from("2020-04-05T12:00"); +assert(dt2.withCalendar(obj).equals(dt)); + +// Temporal.PlainYearMonth.from() +assert.sameValue(`${ ym }`, "2020-04-01[u-ca=two-based]") + +// Temporal.PlainYearMonth fields +assert.sameValue(dt.year, 2020); +assert.sameValue(dt.month, 5); + +// yearmonth.with() +var ym2 = ym.with({ month: 2 }); +assert.sameValue(ym2.month, 2); + +// Temporal.PlainMonthDay.from() +assert.sameValue(`${ md }`, "1972-04-05[u-ca=two-based]") + +// Temporal.PlainMonthDay fields +assert.sameValue(md.monthCode, "M05"); +assert.sameValue(md.day, 5); + +// monthday.with() +var md2 = md.with({ monthCode: "M02" }); +assert.sameValue(md2.monthCode, "M02"); + +// timezone.getPlainDateTimeFor() +var tz = Temporal.TimeZone.from("UTC"); +var instant = Temporal.Instant.fromEpochSeconds(0); +var dt = tz.getPlainDateTimeFor(instant, obj); +assert.sameValue(dt.getCalendar(), obj); + +// Temporal.Now.plainDateTime() +var nowDateTime = Temporal.Now.plainDateTime(obj, "UTC"); +assert.sameValue(nowDateTime.getCalendar(), obj); + +// Temporal.Now.plainDate() +var nowDate = Temporal.Now.plainDate(obj, "UTC"); +assert.sameValue(nowDate.getCalendar(), obj); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/shell.js b/js/src/tests/test262/staging/Temporal/UserCalendar/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserCalendar/shell.js diff --git a/js/src/tests/test262/staging/Temporal/UserTimezone/browser.js b/js/src/tests/test262/staging/Temporal/UserTimezone/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserTimezone/browser.js diff --git a/js/src/tests/test262/staging/Temporal/UserTimezone/old/browser.js b/js/src/tests/test262/staging/Temporal/UserTimezone/old/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserTimezone/old/browser.js diff --git a/js/src/tests/test262/staging/Temporal/UserTimezone/old/shell.js b/js/src/tests/test262/staging/Temporal/UserTimezone/old/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserTimezone/old/shell.js diff --git a/js/src/tests/test262/staging/Temporal/UserTimezone/old/subminute-offset.js b/js/src/tests/test262/staging/Temporal/UserTimezone/old/subminute-offset.js new file mode 100644 index 0000000000..52d11f3548 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserTimezone/old/subminute-offset.js @@ -0,0 +1,106 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: sub-minute offset +features: [Temporal] +---*/ + +class SubminuteTimeZone extends Temporal.TimeZone { + constructor() { + super("UTC"); + } + get id() { + return "Custom/Subminute"; + } + toString() { + return "Custom/Subminute"; + } + getOffsetNanosecondsFor() { + return -1111111111; + } + getPossibleInstantsFor(dateTime) { + var utc = Temporal.TimeZone.from("UTC"); + var instant = utc.getInstantFor(dateTime); + return [instant.add({ nanoseconds: 1111111111 })]; + } + getNextTransition() { + return null; + } + getPreviousTransition() { + return null; + } +} +var obj = new SubminuteTimeZone(); +var inst = Temporal.Instant.fromEpochNanoseconds(0n); +var dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789); + +// is a time zone +assert.sameValue(typeof obj, "object") + +// .id property +assert.sameValue(obj.id, "Custom/Subminute") + +// .id is not available in from() +assert.throws(RangeError, () => Temporal.TimeZone.from("Custom/Subminute")); +assert.throws(RangeError, () => Temporal.TimeZone.from("2020-05-26T16:02:46.251163036-00:00:01.111111111[Custom/Subminute]")); + +// has offset string -00:00:01.111111111 +assert.sameValue(obj.getOffsetStringFor(inst), "-00:00:01.111111111") + +// converts to DateTime +var fakeGregorian = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "gregory", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +assert.sameValue(`${ obj.getPlainDateTimeFor(inst) }`, "1969-12-31T23:59:58.888888889"); +assert.sameValue(`${ obj.getPlainDateTimeFor(inst, fakeGregorian) }`, "1969-12-31T23:59:58.888888889[u-ca=gregory]"); + +// converts to Instant +assert.sameValue(`${ obj.getInstantFor(dt) }`, "1976-11-18T15:23:31.2345679Z"); + +// converts to string +assert.sameValue(`${ obj }`, obj.id) + +// offset prints with minute precision in instant.toString +assert.sameValue(inst.toString({ timeZone: obj }), "1969-12-31T23:59:58.888888889+00:00") + +// offset prints with minute precision prints in zdt.toString +var zdt = new Temporal.ZonedDateTime(0n, obj); +assert.sameValue(zdt.toString(), "1969-12-31T23:59:58.888888889+00:00[Custom/Subminute]"); + +// has no next transitions +assert.sameValue(obj.getNextTransition(), null) + +// has no previous transitions +assert.sameValue(obj.getPreviousTransition(), null) + +// works in Temporal.Now +assert(Temporal.Now.plainDateTimeISO(obj) instanceof Temporal.PlainDateTime); +assert(Temporal.Now.plainDateTime(fakeGregorian, obj) instanceof Temporal.PlainDateTime); +assert(Temporal.Now.plainDateISO(obj) instanceof Temporal.PlainDate); +assert(Temporal.Now.plainDate(fakeGregorian, obj) instanceof Temporal.PlainDate); +assert(Temporal.Now.plainTimeISO(obj) instanceof Temporal.PlainTime); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/UserTimezone/old/trivial-protocol.js b/js/src/tests/test262/staging/Temporal/UserTimezone/old/trivial-protocol.js new file mode 100644 index 0000000000..e11e798f68 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserTimezone/old/trivial-protocol.js @@ -0,0 +1,63 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Trivial protocol implementation +features: [Temporal] +---*/ + +var obj = { + getOffsetNanosecondsFor() { + return 0; + }, + getPossibleInstantsFor(dateTime) { + var {year, month, day, hour, minute, second, millisecond, microsecond, nanosecond} = dateTime; + var dayNum = MakeDay(year, month, day); + var time = MakeTime(hour, minute, second, millisecond, microsecond, nanosecond); + var epochNs = MakeDate(dayNum, time); + return [new Temporal.Instant(epochNs)]; + }, + id: "Etc/Custom/UTC_Protocol", +}; +var inst = Temporal.Instant.fromEpochNanoseconds(0n); + +// offset prints in instant.toString +assert.sameValue(inst.toString({ timeZone: obj }), "1970-01-01T00:00:00+00:00") + +// prints in zdt.toString +var zdt = new Temporal.ZonedDateTime(0n, obj); +assert.sameValue(zdt.toString(), "1970-01-01T00:00:00+00:00[Etc/Custom/UTC_Protocol]"); + +// works in Temporal.Now +var fakeGregorian = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "gregory", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +assert(Temporal.Now.plainDateTimeISO(obj) instanceof Temporal.PlainDateTime); +assert(Temporal.Now.plainDateTime(fakeGregorian, obj) instanceof Temporal.PlainDateTime); +assert(Temporal.Now.plainDateISO(obj) instanceof Temporal.PlainDate); +assert(Temporal.Now.plainDate(fakeGregorian, obj) instanceof Temporal.PlainDate); +assert(Temporal.Now.plainTimeISO(obj) instanceof Temporal.PlainTime); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/UserTimezone/old/trivial-subclass.js b/js/src/tests/test262/staging/Temporal/UserTimezone/old/trivial-subclass.js new file mode 100644 index 0000000000..6b6db93e35 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserTimezone/old/trivial-subclass.js @@ -0,0 +1,144 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Trivial subclass +features: [Temporal] +---*/ + +class CustomUTCSubclass extends Temporal.TimeZone { + constructor() { + super("UTC"); + } + get id() { + return "Etc/Custom/UTC_Subclass"; + } + toString() { + return "Etc/Custom/UTC_Subclass"; + } + getOffsetNanosecondsFor() { + return 0; + } + getPossibleInstantsFor(dateTime) { + var {year, month, day, hour, minute, second, millisecond, microsecond, nanosecond} = dateTime; + var dayNum = MakeDay(year, month, day); + var time = MakeTime(hour, minute, second, millisecond, microsecond, nanosecond); + var epochNs = MakeDate(dayNum, time); + return [new Temporal.Instant(epochNs)]; + } + getNextTransition() { + return null; + } + getPreviousTransition() { + return null; + } +} + +const nsPerDay = 86400_000_000_000n; +const nsPerMillisecond = 1_000_000n; + +function Day(t) { + return t / nsPerDay; +} + +function MakeDate(day, time) { + return day * nsPerDay + time; +} + +function MakeDay(year, month, day) { + const m = month - 1; + const ym = year + Math.floor(m / 12); + const mn = m % 12; + const t = BigInt(Date.UTC(ym, mn, 1)) * nsPerMillisecond; + return Day(t) + BigInt(day) - 1n; +} + +function MakeTime(h, min, s, ms, µs, ns) { + const MinutesPerHour = 60n; + const SecondsPerMinute = 60n; + const nsPerSecond = 1_000_000_000n; + const nsPerMinute = nsPerSecond * SecondsPerMinute; + const nsPerHour = nsPerMinute * MinutesPerHour; + return ( + BigInt(h) * nsPerHour + + BigInt(min) * nsPerMinute + + BigInt(s) * nsPerSecond + + BigInt(ms) * nsPerMillisecond + + BigInt(µs) * 1000n + + BigInt(ns) + ); +} + +var obj = new CustomUTCSubclass(); +var inst = Temporal.Instant.fromEpochNanoseconds(0n); +var dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789); + +// is a time zone +assert.sameValue(typeof obj, "object") + +// .id property +assert.sameValue(obj.id, "Etc/Custom/UTC_Subclass") + +// .id is not available in from() +assert.throws(RangeError, () => Temporal.TimeZone.from("Etc/Custom/UTC_Subclass")); +assert.throws(RangeError, () => Temporal.TimeZone.from("2020-05-26T16:02:46.251163036+00:00[Etc/Custom/UTC_Subclass]")); + +// has offset string +00:00 +assert.sameValue(obj.getOffsetStringFor(inst), "+00:00") + +// converts to DateTime +var fakeGregorian = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "gregory", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +assert.sameValue(`${ obj.getPlainDateTimeFor(inst) }`, "1970-01-01T00:00:00"); +assert.sameValue(`${ obj.getPlainDateTimeFor(inst, fakeGregorian) }`, "1970-01-01T00:00:00[u-ca=gregory]"); + +// converts to Instant +assert.sameValue(`${ obj.getInstantFor(dt) }`, "1976-11-18T15:23:30.123456789Z"); + +// converts to string +assert.sameValue(`${ obj }`, obj.id) + +// offset prints in instant.toString +assert.sameValue(inst.toString({ timeZone: obj }), "1970-01-01T00:00:00+00:00") + +// prints in zdt.toString +var zdt = new Temporal.ZonedDateTime(0n, obj); +assert.sameValue(zdt.toString(), "1970-01-01T00:00:00+00:00[Etc/Custom/UTC_Subclass]"); + +// has no next transitions +assert.sameValue(obj.getNextTransition(), null) + +// has no previous transitions +assert.sameValue(obj.getPreviousTransition(), null) + +// works in Temporal.Now +assert(Temporal.Now.plainDateTimeISO(obj) instanceof Temporal.PlainDateTime); +assert(Temporal.Now.plainDateTime(fakeGregorian, obj) instanceof Temporal.PlainDateTime); +assert(Temporal.Now.plainDateISO(obj) instanceof Temporal.PlainDate); +assert(Temporal.Now.plainDate(fakeGregorian, obj) instanceof Temporal.PlainDate); +assert(Temporal.Now.plainTimeISO(obj) instanceof Temporal.PlainTime); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/UserTimezone/shell.js b/js/src/tests/test262/staging/Temporal/UserTimezone/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserTimezone/shell.js diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/browser.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/browser.js diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/add.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/add.js new file mode 100644 index 0000000000..e1fdbd9ac5 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/add.js @@ -0,0 +1,53 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.add() +features: [Temporal] +---*/ + +var zdt = Temporal.ZonedDateTime.from("1969-12-25T12:23:45.678901234+00:00[UTC]"); +// cross epoch in ms + var one = zdt.subtract({ + hours: 240, + nanoseconds: 800 + }); + var two = zdt.add({ + hours: 240, + nanoseconds: 800 + }); + var three = two.subtract({ + hours: 480, + nanoseconds: 1600 + }); + var four = one.add({ + hours: 480, + nanoseconds: 1600 + }); +assert.sameValue(`${ one }`, "1969-12-15T12:23:45.678900434+00:00[UTC]"); +assert.sameValue(`${ two }`, "1970-01-04T12:23:45.678902034+00:00[UTC]"); +assert(three.equals(one)); +assert(four.equals(two)); + +// zdt.add(durationObj) +var later = zdt.add(Temporal.Duration.from("PT240H0.000000800S")); +assert.sameValue(`${ later }`, "1970-01-04T12:23:45.678902034+00:00[UTC]"); + +// casts argument +assert.sameValue(`${ zdt.add("PT240H0.000000800S") }`, "1970-01-04T12:23:45.678902034+00:00[UTC]"); +var jan31 = Temporal.ZonedDateTime.from("2020-01-31T15:00-08:00[-08:00]"); + +// constrain when ambiguous result +assert.sameValue(`${ jan31.add({ months: 1 }) }`, "2020-02-29T15:00:00-08:00[-08:00]"); +assert.sameValue(`${ jan31.add({ months: 1 }, { overflow: "constrain" }) }`, "2020-02-29T15:00:00-08:00[-08:00]"); + +// symmetrical with regard to negative durations in the time part +assert.sameValue(`${ jan31.add({ minutes: -30 }) }`, "2020-01-31T14:30:00-08:00[-08:00]"); +assert.sameValue(`${ jan31.add({ seconds: -30 }) }`, "2020-01-31T14:59:30-08:00[-08:00]"); + +// throw when ambiguous result with reject +assert.throws(RangeError, () => jan31.add({ months: 1 }, { overflow: "reject" })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/browser.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/browser.js diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/compare.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/compare.js new file mode 100644 index 0000000000..7345c962b5 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/compare.js @@ -0,0 +1,146 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.compare() +features: [Temporal] +---*/ + +var zdt1 = Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456789+01:00[+01:00]"); +var zdt2 = Temporal.ZonedDateTime.from("2019-10-29T10:46:38.271986102+01:00[+01:00]"); + +// equal +assert.sameValue(Temporal.ZonedDateTime.compare(zdt1, zdt1), 0) + +// smaller/larger +assert.sameValue(Temporal.ZonedDateTime.compare(zdt1, zdt2), -1) + +// larger/smaller +assert.sameValue(Temporal.ZonedDateTime.compare(zdt2, zdt1), 1) + +// casts first argument +assert.sameValue(Temporal.ZonedDateTime.compare({ + year: 1976, + month: 11, + day: 18, + hour: 15, + timeZone: "+01:00" +}, zdt2), -1); +assert.sameValue(Temporal.ZonedDateTime.compare("1976-11-18T15:23:30.123456789+01:00[+01:00]", zdt2), -1); + +// casts second argument +assert.sameValue(Temporal.ZonedDateTime.compare(zdt1, { + year: 2019, + month: 10, + day: 29, + hour: 10, + timeZone: "+01:00" +}), -1); +assert.sameValue(Temporal.ZonedDateTime.compare(zdt1, "2019-10-29T10:46:38.271986102+01:00[+01:00]"), -1); + +// object must contain at least the required properties +assert.sameValue(Temporal.ZonedDateTime.compare({ + year: 1976, + month: 11, + day: 18, + timeZone: "+01:00" +}, zdt2), -1); +assert.throws(TypeError, () => Temporal.ZonedDateTime.compare({ + month: 11, + day: 18, + timeZone: "+01:00" +}, zdt2)); +assert.throws(TypeError, () => Temporal.ZonedDateTime.compare({ + year: 1976, + day: 18, + timeZone: "+01:00" +}, zdt2)); +assert.throws(TypeError, () => Temporal.ZonedDateTime.compare({ + year: 1976, + month: 11, + timeZone: "+01:00" +}, zdt2)); +assert.throws(TypeError, () => Temporal.ZonedDateTime.compare({ + year: 1976, + month: 11, + day: 18 +}, zdt2)); +assert.throws(TypeError, () => Temporal.ZonedDateTime.compare({ + years: 1976, + months: 11, + days: 19, + hours: 15, + timeZone: "+01:00" +}, zdt2)); +assert.sameValue(Temporal.ZonedDateTime.compare(zdt1, { + year: 2019, + month: 10, + day: 29, + timeZone: "+01:00" +}), -1); +assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(zdt1, { + month: 10, + day: 29, + timeZone: "+01:00" +})); +assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(zdt1, { + year: 2019, + day: 29, + timeZone: "+01:00" +})); +assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(zdt1, { + year: 2019, + month: 10, + timeZone: "+01:00" +})); +assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(zdt1, { + year: 2019, + month: 10, + day: 29 +})); +assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(zdt1, { + years: 2019, + months: 10, + days: 29, + hours: 10, + timeZone: "+01:00" +})); + +// disregards time zone IDs if exact times are equal +assert.sameValue(Temporal.ZonedDateTime.compare(zdt1, zdt1.withTimeZone("+05:30")), 0); + +// disregards calendar IDs if exact times and time zones are equal +var fakeJapanese = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "japanese", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +assert.sameValue(Temporal.ZonedDateTime.compare(zdt1, zdt1.withCalendar(fakeJapanese)), 0); + +// compares exact time, not clock time +var clockBefore = Temporal.ZonedDateTime.from("1999-12-31T23:30-08:00[-08:00]"); +var clockAfter = Temporal.ZonedDateTime.from("2000-01-01T01:30-04:00[-04:00]"); +assert.sameValue(Temporal.ZonedDateTime.compare(clockBefore, clockAfter), 1); +assert.sameValue(Temporal.PlainDateTime.compare(clockBefore.toPlainDateTime(), clockAfter.toPlainDateTime()), -1); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/construction-and-properties.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/construction-and-properties.js new file mode 100644 index 0000000000..eb039a9ee5 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/construction-and-properties.js @@ -0,0 +1,117 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Construction and properties +features: [Temporal] +---*/ + +var tz = new Temporal.TimeZone("-08:00"); +var epochMillis = Date.UTC(1976, 10, 18, 15, 23, 30, 123); +var epochNanos = BigInt(epochMillis) * BigInt(1000000) + BigInt(456789); + +// works +var zdt = new Temporal.ZonedDateTime(epochNanos, tz); +assert(zdt instanceof Temporal.ZonedDateTime); +assert.sameValue(typeof zdt, "object"); +assert.sameValue(zdt.toInstant().epochSeconds, Math.floor(Date.UTC(1976, 10, 18, 15, 23, 30, 123) / 1000), "epochSeconds"); +assert.sameValue(zdt.toInstant().epochMilliseconds, Date.UTC(1976, 10, 18, 15, 23, 30, 123), "epochMilliseconds"); + +// Temporal.ZonedDateTime for (1976, 11, 18, 15, 23, 30, 123, 456, 789)" + var zdt = new Temporal.ZonedDateTime(epochNanos, "UTC"); +// can be constructed +assert(zdt instanceof Temporal.ZonedDateTime); +assert.sameValue(typeof zdt, "object"); + +assert.sameValue(zdt.year, 1976) +assert.sameValue(zdt.month, 11); +assert.sameValue(zdt.monthCode, "M11"); +assert.sameValue(zdt.day, 18); +assert.sameValue(zdt.hour, 15); +assert.sameValue(zdt.minute, 23); +assert.sameValue(zdt.second, 30); +assert.sameValue(zdt.millisecond, 123); +assert.sameValue(zdt.microsecond, 456); +assert.sameValue(zdt.nanosecond, 789); +assert.sameValue(zdt.epochSeconds, 217178610); +assert.sameValue(zdt.epochMilliseconds, 217178610123); +assert.sameValue(zdt.epochMicroseconds, 217178610123456n); +assert.sameValue(zdt.epochNanoseconds, 217178610123456789n); +assert.sameValue(zdt.dayOfWeek, 4); +assert.sameValue(zdt.dayOfYear, 323); +assert.sameValue(zdt.weekOfYear, 47); +assert.sameValue(zdt.daysInWeek, 7); +assert.sameValue(zdt.daysInMonth, 30); +assert.sameValue(zdt.daysInYear, 366); +assert.sameValue(zdt.monthsInYear, 12); +assert.sameValue(zdt.inLeapYear, true); +assert.sameValue(zdt.offset, "+00:00"); +assert.sameValue(zdt.offsetNanoseconds, 0); +assert.sameValue(`${ zdt }`, "1976-11-18T15:23:30.123456789+00:00[UTC]"); + +// Temporal.ZonedDateTime with non-UTC time zone and non-ISO calendar +// can be constructed +var fakeGregorian = { + era() { return "ce"; }, + year(date) { return date.withCalendar("iso8601").year; }, + month(date) { return date.withCalendar("iso8601").month; }, + monthCode(date) { return date.withCalendar("iso8601").monthCode; }, + day(date) { return date.withCalendar("iso8601").day; }, + dayOfWeek(date) { return date.withCalendar("iso8601").dayOfWeek; }, + dayOfYear(date) { return date.withCalendar("iso8601").dayOfYear; }, + weekOfYear(date) { return date.withCalendar("iso8601").weekOfYear; }, + daysInWeek(date) { return date.withCalendar("iso8601").daysInWeek; }, + daysInMonth(date) { return date.withCalendar("iso8601").daysInMonth; }, + daysInYear(date) { return date.withCalendar("iso8601").daysInYear; }, + monthsInYear(date) { return date.withCalendar("iso8601").monthsInYear; }, + inLeapYear(date) { return date.withCalendar("iso8601").inLeapYear; }, + id: "gregory", + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + fields() {}, + mergeFields() {}, + monthDayFromFields() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +var fakeVienna = { + getOffsetNanosecondsFor() { return 3600_000_000_000; }, + getPossibleInstantsFor(datetime) { return [datetime.toZonedDateTime("+01:00").toInstant()]; }, + id: "Europe/Vienna", +} +var zdt = new Temporal.ZonedDateTime(epochNanos, fakeVienna, fakeGregorian); +assert(zdt instanceof Temporal.ZonedDateTime); +assert.sameValue(typeof zdt, "object"); + +assert.sameValue(zdt.era, "ce"); +assert.sameValue(zdt.year, 1976); +assert.sameValue(zdt.month, 11); +assert.sameValue(zdt.monthCode, "M11"); +assert.sameValue(zdt.day, 18); +assert.sameValue(zdt.hour, 16); +assert.sameValue(zdt.minute, 23); +assert.sameValue(zdt.second, 30); +assert.sameValue(zdt.millisecond, 123); +assert.sameValue(zdt.microsecond, 456); +assert.sameValue(zdt.nanosecond, 789); +assert.sameValue(zdt.epochSeconds, 217178610); +assert.sameValue(zdt.epochMilliseconds, 217178610123); +assert.sameValue(zdt.epochMicroseconds, 217178610123456n); +assert.sameValue(zdt.epochNanoseconds, 217178610123456789n); +assert.sameValue(zdt.dayOfWeek, 4); +assert.sameValue(zdt.dayOfYear, 323); +assert.sameValue(zdt.weekOfYear, 47); +assert.sameValue(zdt.daysInWeek, 7); +assert.sameValue(zdt.daysInMonth, 30); +assert.sameValue(zdt.daysInYear, 366); +assert.sameValue(zdt.monthsInYear, 12); +assert.sameValue(zdt.inLeapYear, true); +assert.sameValue(zdt.offset, "+01:00"); +assert.sameValue(zdt.offsetNanoseconds, 3600000000000); +assert.sameValue(`${ zdt }`, "1976-11-18T16:23:30.123456789+01:00[Europe/Vienna][u-ca=gregory]"); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/date-time-hours-overflow.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/date-time-hours-overflow.js new file mode 100644 index 0000000000..5c1f644c1b --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/date-time-hours-overflow.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: hours overflow +features: [Temporal] +---*/ + + +// subtract result +var later = Temporal.ZonedDateTime.from("2019-10-29T10:46:38.271986102-03:00[-03:00]"); +var earlier = later.subtract({ hours: 12 }); +assert.sameValue(`${ earlier }`, "2019-10-28T22:46:38.271986102-03:00[-03:00]"); + +// add result +var earlier = Temporal.ZonedDateTime.from("2020-05-31T23:12:38.271986102-04:00[-04:00]"); +var later = earlier.add({ hours: 2 }); +assert.sameValue(`${ later }`, "2020-06-01T01:12:38.271986102-04:00[-04:00]"); + +// symmetrical with regard to negative durations +assert.sameValue(`${ Temporal.ZonedDateTime.from("2019-10-29T10:46:38.271986102-03:00[-03:00]").add({ hours: -12 }) }`, "2019-10-28T22:46:38.271986102-03:00[-03:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("2020-05-31T23:12:38.271986102-04:00[-04:00]").subtract({ hours: -2 }) }`, "2020-06-01T01:12:38.271986102-04:00[-04:00]"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/dst-math.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/dst-math.js new file mode 100644 index 0000000000..6e8879ee65 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/dst-math.js @@ -0,0 +1,279 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: math around DST +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var tz = TemporalHelpers.springForwardFallBackTimeZone(); +var hourBeforeDstStart = new Temporal.PlainDateTime(2000, 4, 2, 1).toZonedDateTime(tz); +var dayBeforeDstStart = new Temporal.PlainDateTime(2000, 4, 1, 2, 30).toZonedDateTime(tz); + +// add 1 hour to get to DST start +var added = hourBeforeDstStart.add({ hours: 1 }); +assert.sameValue(added.hour, 3); +var diff = hourBeforeDstStart.until(added, { largestUnit: "hours" }); +assert.sameValue(`${ diff }`, "PT1H"); +assert.sameValue(`${ diff }`, `${ added.since(hourBeforeDstStart, { largestUnit: "hours" }) }`); +var undo = added.subtract(diff); +assert.sameValue(`${ undo }`, `${ hourBeforeDstStart }`); + +// add 2 hours to get to DST start +1 +var added = hourBeforeDstStart.add({ hours: 2 }); +assert.sameValue(added.hour, 4); +var diff = hourBeforeDstStart.until(added, { largestUnit: "hours" }); +assert.sameValue(`${ diff }`, "PT2H"); +assert.sameValue(`${ diff }`, `${ added.since(hourBeforeDstStart, { largestUnit: "hours" }) }`); +var undo = added.subtract(diff); +assert.sameValue(`${ undo }`, `${ hourBeforeDstStart }`); + +// add 1.5 hours to get to 0.5 hours after DST start +var added = hourBeforeDstStart.add({ + hours: 1, + minutes: 30 +}); +assert.sameValue(added.hour, 3); +assert.sameValue(added.minute, 30); +var diff = hourBeforeDstStart.until(added, { largestUnit: "hours" }); +assert.sameValue(`${ diff }`, "PT1H30M"); +assert.sameValue(`${ diff }`, `${ added.since(hourBeforeDstStart, { largestUnit: "hours" }) }`); +var undo = added.subtract(diff); +assert.sameValue(`${ undo }`, `${ hourBeforeDstStart }`); + +// Samoa date line change (add): 10:00PM 29 Dec 2011 -> 11:00PM 31 Dec 2011 +var timeZone = TemporalHelpers.crossDateLineTimeZone(); +var dayBeforeSamoaDateLineChangeAbs = timeZone.getInstantFor(new Temporal.PlainDateTime(2011, 12, 29, 22)); +var start = dayBeforeSamoaDateLineChangeAbs.toZonedDateTimeISO(timeZone); +var added = start.add({ + days: 1, + hours: 1 +}); +assert.sameValue(added.day, 31); +assert.sameValue(added.hour, 23); +assert.sameValue(added.minute, 0); +var diff = start.until(added, { largestUnit: "days" }); +assert.sameValue(`${ diff }`, "P2DT1H"); +var undo = added.subtract(diff); +assert.sameValue(`${ undo }`, `${ start }`); + +// Samoa date line change (subtract): 11:00PM 31 Dec 2011 -> 10:00PM 29 Dec 2011 +var dayAfterSamoaDateLineChangeAbs = timeZone.getInstantFor(new Temporal.PlainDateTime(2011, 12, 31, 23)); +var start = dayAfterSamoaDateLineChangeAbs.toZonedDateTimeISO(timeZone); +var skipped = start.subtract({ + days: 1, + hours: 1 +}); +assert.sameValue(skipped.day, 31); +assert.sameValue(skipped.hour, 22); +assert.sameValue(skipped.minute, 0); +var end = start.subtract({ + days: 2, + hours: 1 +}); +assert.sameValue(end.day, 29); +assert.sameValue(end.hour, 22); +assert.sameValue(end.minute, 0); +var diff = end.since(start, { largestUnit: "days" }); +assert.sameValue(`${ diff }`, "-P2DT1H"); +var undo = start.add(diff); +assert.sameValue(`${ undo }`, `${ end }`); + +// 3:30 day before DST start -> 3:30 day of DST start +var start = dayBeforeDstStart.add({ hours: 1 }); +var added = start.add({ days: 1 }); +assert.sameValue(added.day, 2); +assert.sameValue(added.hour, 3); +assert.sameValue(added.minute, 30); +var diff = start.until(added, { largestUnit: "days" }); +assert.sameValue(`${ diff }`, "P1D"); +var undo = added.subtract(diff); +assert.sameValue(`${ undo }`, `${ start }`); + +// 2:30 day before DST start -> 3:30 day of DST start +var added = dayBeforeDstStart.add({ days: 1 }); +assert.sameValue(added.day, 2); +assert.sameValue(added.hour, 3); +assert.sameValue(added.minute, 30); +var diff = dayBeforeDstStart.until(added, { largestUnit: "days" }); +assert.sameValue(`${ diff }`, "P1D"); +var undo = dayBeforeDstStart.add(diff); +assert.sameValue(`${ undo }`, `${ added }`); + +// 1:30 day DST starts -> 4:30 day DST starts +var start = dayBeforeDstStart.add({ hours: 23 }); +var added = start.add({ hours: 2 }); +assert.sameValue(added.day, 2); +assert.sameValue(added.hour, 4); +assert.sameValue(added.minute, 30); +var diff = start.until(added, { largestUnit: "days" }); +assert.sameValue(`${ diff }`, "PT2H"); +var undo = added.subtract(diff); +assert.sameValue(`${ undo }`, `${ start }`); + +// 2:00 day before DST starts -> 3:00 day DST starts +var start = hourBeforeDstStart.subtract({ days: 1 }).add({ hours: 1 }); +var added = start.add({ days: 1 }); +assert.sameValue(added.day, 2); +assert.sameValue(added.hour, 3); +assert.sameValue(added.minute, 0); +var diff = start.until(added, { largestUnit: "days" }); +assert.sameValue(`${ diff }`, "P1D"); +var undo = start.add(diff); +assert.sameValue(`${ undo }`, `${ added }`); + +// 1:00AM day DST starts -> (add 24 hours) -> 2:00AM day after DST starts +var start = hourBeforeDstStart; +var added = start.add({ hours: 24 }); +assert.sameValue(added.day, 3); +assert.sameValue(added.hour, 2); +assert.sameValue(added.minute, 0); +var diff = start.until(added, { largestUnit: "days" }); +assert.sameValue(`${ diff }`, "P1DT1H"); +var undo = added.subtract(diff); +assert.sameValue(`${ undo }`, `${ start }`); + +// 12:00AM day DST starts -> (add 24 hours) -> 1:00AM day after DST starts +var start = hourBeforeDstStart.subtract({ hours: 1 }); +var added = start.add({ hours: 24 }); +assert.sameValue(added.day, 3); +assert.sameValue(added.hour, 1); +assert.sameValue(added.minute, 0); +var diff = start.until(added, { largestUnit: "days" }); +assert.sameValue(`${ diff }`, "P1DT1H"); +var undo = added.subtract(diff); +assert.sameValue(`${ undo }`, `${ start }`); + +// Difference can return day length > 24 hours +var start = Temporal.PlainDateTime.from("2000-10-27T01:45").toZonedDateTime(tz); +var end = Temporal.PlainDateTime.from("2000-10-30T01:15").toZonedDateTime(tz); +var diff = start.until(end, { largestUnit: "days" }); +assert.sameValue(`${ diff }`, "P2DT24H30M"); +var undo = start.add(diff); +assert.sameValue(`${ undo }`, `${ end }`); + +// Difference rounding (nearest day) is DST-aware +var start = Temporal.PlainDateTime.from("2000-04-04T02:30").toZonedDateTime(tz); +var end = Temporal.PlainDateTime.from("2000-04-01T14:15").toZonedDateTime(tz); +var diff = start.until(end, { + smallestUnit: "days", + roundingMode: "halfExpand" +}); +assert.sameValue(`${ diff }`, "-P3D"); + +// Difference rounding (ceil day) is DST-aware +var diff = start.until(end, { + smallestUnit: "days", + roundingMode: "ceil" +}); +assert.sameValue(`${ diff }`, "-P2D"); + +// Difference rounding (trunc day) is DST-aware +var diff = start.until(end, { + smallestUnit: "days", + roundingMode: "trunc" +}); +assert.sameValue(`${ diff }`, "-P2D"); + +// Difference rounding (floor day) is DST-aware +var diff = start.until(end, { + smallestUnit: "days", + roundingMode: "floor" +}); +assert.sameValue(`${ diff }`, "-P3D"); + +// Difference rounding (nearest hour) is DST-aware +var diff = start.until(end, { + largestUnit: "days", + smallestUnit: "hours", + roundingMode: "halfExpand" +}); +assert.sameValue(`${ diff }`, "-P2DT12H"); + +// Difference rounding (ceil hour) is DST-aware +var diff = start.until(end, { + largestUnit: "days", + smallestUnit: "hours", + roundingMode: "ceil" +}); +assert.sameValue(`${ diff }`, "-P2DT12H"); + +// Difference rounding (trunc hour) is DST-aware +var diff = start.until(end, { + largestUnit: "days", + smallestUnit: "hours", + roundingMode: "trunc" +}); +assert.sameValue(`${ diff }`, "-P2DT12H"); + +// Difference rounding (floor hour) is DST-aware +var diff = start.until(end, { + largestUnit: "days", + smallestUnit: "hours", + roundingMode: "floor" +}); +assert.sameValue(`${ diff }`, "-P2DT13H"); + +// Difference when date portion ends inside a DST-skipped period +var start = Temporal.PlainDateTime.from("2000-04-01T02:30").toZonedDateTime(tz); +var end = Temporal.PlainDateTime.from("2000-04-02T03:15").toZonedDateTime(tz); +var diff = start.until(end, { largestUnit: "days" }); +assert.sameValue(`${ diff }`, "PT23H45M"); + +// Difference when date portion ends inside day skipped by Samoa's 24hr 2011 transition +var end = Temporal.PlainDateTime.from("2011-12-31T05:00").toZonedDateTime(timeZone); +var start = Temporal.PlainDateTime.from("2011-12-28T10:00").toZonedDateTime(timeZone); +var diff = start.until(end, { largestUnit: "days" }); +assert.sameValue(`${ diff }`, "P1DT19H"); + +// Rounding up to hours causes one more day of overflow (positive) +var start = Temporal.ZonedDateTime.from("2020-01-01T00:00-08:00[-08:00]"); +var end = Temporal.ZonedDateTime.from("2020-01-03T23:59-08:00[-08:00]"); +var diff = start.until(end, { + largestUnit: "days", + smallestUnit: "hours", + roundingMode: "halfExpand" +}); +assert.sameValue(`${ diff }`, "P3D"); + +// Rounding up to hours causes one more day of overflow (negative) +var start = Temporal.ZonedDateTime.from("2020-01-01T00:00-08:00[-08:00]"); +var end = Temporal.ZonedDateTime.from("2020-01-03T23:59-08:00[-08:00]"); +var diff = end.until(start, { + largestUnit: "days", + smallestUnit: "hours", + roundingMode: "halfExpand" +}); +assert.sameValue(`${ diff }`, "-P3D"); + +// addition and difference work near DST start +var stepsPerHour = 2; +var minutesPerStep = 60 / stepsPerHour; +var hoursUntilEnd = 26; +var startHourRange = 3; +for (var i = 0; i < startHourRange * stepsPerHour; i++) { + var start = hourBeforeDstStart.add({ minutes: minutesPerStep * i }); + for (var j = 0; j < hoursUntilEnd * stepsPerHour; j++) { + var end = start.add({ minutes: j * minutesPerStep }); + var diff = start.until(end, { largestUnit: "days" }); + var expectedMinutes = minutesPerStep * (j % stepsPerHour); + assert.sameValue(diff.minutes, expectedMinutes); + var diff60 = Math.floor(j / stepsPerHour); + if (i >= stepsPerHour) { + var expectedDays = diff60 < 24 ? 0 : diff60 < 48 ? 1 : 2; + var expectedHours = diff60 < 24 ? diff60 : diff60 < 48 ? diff60 - 24 : diff60 - 48; + assert.sameValue(diff.hours, expectedHours); + assert.sameValue(diff.days, expectedDays); + } else { + var expectedDays = diff60 < 23 ? 0 : diff60 < 47 ? 1 : 2; + var expectedHours = diff60 < 23 ? diff60 : diff60 < 47 ? diff60 - 23 : diff60 - 47; + assert.sameValue(diff.hours, expectedHours); + assert.sameValue(diff.days, expectedDays); + } + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/dst-properties.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/dst-properties.js new file mode 100644 index 0000000000..fbd5d37197 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/dst-properties.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: properties around DST +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var tz = TemporalHelpers.springForwardFallBackTimeZone(); +var hourBeforeDstStart = new Temporal.PlainDateTime(2000, 4, 2, 1).toZonedDateTime(tz); +var dayBeforeDstStart = new Temporal.PlainDateTime(2000, 4, 1, 2, 30).toZonedDateTime(tz); + +// hoursInDay works with DST start +assert.sameValue(hourBeforeDstStart.hoursInDay, 23); + +// hoursInDay works with non-DST days +assert.sameValue(dayBeforeDstStart.hoursInDay, 24); + +// hoursInDay works with DST end +var dstEnd = Temporal.PlainDateTime.from("2000-10-29T01:00").toZonedDateTime(tz); +assert.sameValue(dstEnd.hoursInDay, 25); + +// startOfDay works +var start = dayBeforeDstStart.startOfDay(); +assert.sameValue(`${ start.toPlainDate() }`, `${ dayBeforeDstStart.toPlainDate() }`); +assert.sameValue(`${ start.toPlainTime() }`, "00:00:00"); + +var samoa = TemporalHelpers.crossDateLineTimeZone(); +var dayAfterSamoaDateLineChange = Temporal.PlainDateTime.from("2011-12-31T22:00").toZonedDateTime(samoa); +var dayBeforeSamoaDateLineChange = Temporal.PlainDateTime.from("2011-12-29T22:00").toZonedDateTime(samoa); + +// startOfDay works after Samoa date line change +var start = dayAfterSamoaDateLineChange.startOfDay(); +assert.sameValue(`${ start.toPlainTime() }`, "00:00:00"); + +// hoursInDay works after Samoa date line change +assert.sameValue(dayAfterSamoaDateLineChange.hoursInDay, 24); + +// hoursInDay works before Samoa date line change +assert.sameValue(dayBeforeSamoaDateLineChange.hoursInDay, 24); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/equals.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/equals.js new file mode 100644 index 0000000000..9f3eb5515e --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/equals.js @@ -0,0 +1,111 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.equals() +features: [Temporal] +---*/ + +var tz = { + getOffsetNanosecondsFor() { return -5 * 3600_000_000_000; }, + getPossibleInstantsFor(pdt) { return Temporal.TimeZone.from("-05:00").getPossibleInstantsFor(pdt); }, + id: "America/New_York", +}; +var cal = { + dateFromFields(...args) { return Temporal.Calendar.from("iso8601").dateFromFields(...args); }, + id: "gregory", + dateAdd() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields(fieldNames) { return fieldNames; }, + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +var zdt = new Temporal.ZonedDateTime(0n, tz, cal); + +// constructed from equivalent parameters are equal +var zdt2 = Temporal.ZonedDateTime.from({ + year: 1969, + month: 12, + day: 31, + hour: 19, + timeZone: tz, + calendar: cal, +}); +assert(zdt.equals(zdt2)); +assert(zdt2.equals(zdt)); + +// different instant not equal +var zdt2 = new Temporal.ZonedDateTime(1n, tz, cal); +assert(!zdt.equals(zdt2)); + +// different time zone not equal +var zdt2 = new Temporal.ZonedDateTime(0n, "UTC", cal); +assert(!zdt.equals(zdt2)); + +// different calendar not equal +var zdt2 = new Temporal.ZonedDateTime(0n, tz, "iso8601"); +assert(!zdt.equals(zdt2)); + +// casts its argument +var instance = new Temporal.ZonedDateTime(0n, "UTC", "iso8601"); +assert(instance.equals("1970-01-01T00:00+00:00[UTC][u-ca=iso8601]")); +assert(instance.equals({ + year: 1970, + month: 1, + day: 1, + timeZone: "UTC", + calendar: "iso8601", +})); + +// at least the required properties must be present +assert(!zdt.equals({ + year: 1969, + month: 12, + day: 31, + timeZone: tz +})); +assert.throws(TypeError, () => zdt.equals({ + month: 12, + day: 31, + timeZone: tz +})); +assert.throws(TypeError, () => zdt.equals({ + year: 1969, + day: 31, + timeZone: tz +})); +assert.throws(TypeError, () => zdt.equals({ + year: 1969, + month: 12, + timeZone: tz +})); +assert.throws(TypeError, () => zdt.equals({ + year: 1969, + month: 12, + day: 31 +})); +assert.throws(TypeError, () => zdt.equals({ + years: 1969, + months: 12, + days: 31, + timeZone: tz, + calendarName: "gregory" +})); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/order-of-operations.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/order-of-operations.js new file mode 100644 index 0000000000..406ba04865 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/order-of-operations.js @@ -0,0 +1,82 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: math order of operations and options +features: [Temporal] +---*/ + +var breakoutUnits = (op, zdt, d, options) => zdt[op]({ years: d.years }, options)[op]({ months: d.months }, options)[op]({ weeks: d.weeks }, options)[op]({ days: d.days }, options)[op]({ + hours: d.hours, + minutes: d.minutes, + seconds: d.seconds, + milliseconds: d.milliseconds, + microseconds: d.microseconds, + nanoseconds: d.nanoseconds +}, options); + +// order of operations: add / none +var zdt = Temporal.ZonedDateTime.from("2020-01-31T00:00-08:00[-08:00]"); +var d = Temporal.Duration.from({ + months: 1, + days: 1 +}); +var options = undefined; +var result = zdt.add(d, options); +assert.sameValue(result.toString(), "2020-03-01T00:00:00-08:00[-08:00]"); +assert.sameValue(breakoutUnits("add", zdt, d, options).toString(), result.toString()); + +// order of operations: add / constrain +var zdt = Temporal.ZonedDateTime.from("2020-01-31T00:00-08:00[-08:00]"); +var d = Temporal.Duration.from({ + months: 1, + days: 1 +}); +var options = { overflow: "constrain" }; +var result = zdt.add(d, options); +assert.sameValue(result.toString(), "2020-03-01T00:00:00-08:00[-08:00]"); +assert.sameValue(breakoutUnits("add", zdt, d, options).toString(), result.toString()); + +// order of operations: add / reject +var zdt = Temporal.ZonedDateTime.from("2020-01-31T00:00-08:00[-08:00]"); +var d = Temporal.Duration.from({ + months: 1, + days: 1 +}); +var options = { overflow: "reject" }; +assert.throws(RangeError, () => zdt.add(d, options)); + +// order of operations: subtract / none +var zdt = Temporal.ZonedDateTime.from("2020-03-31T00:00-08:00[-08:00]"); +var d = Temporal.Duration.from({ + months: 1, + days: 1 +}); +var options = undefined; +var result = zdt.subtract(d, options); +assert.sameValue(result.toString(), "2020-02-28T00:00:00-08:00[-08:00]"); +assert.sameValue(breakoutUnits("subtract", zdt, d, options).toString(), result.toString()); + +// order of operations: subtract / constrain +var zdt = Temporal.ZonedDateTime.from("2020-03-31T00:00-08:00[-08:00]"); +var d = Temporal.Duration.from({ + months: 1, + days: 1 +}); +var options = { overflow: "constrain" }; +var result = zdt.subtract(d, options); +assert.sameValue(result.toString(), "2020-02-28T00:00:00-08:00[-08:00]"); +assert.sameValue(breakoutUnits("subtract", zdt, d, options).toString(), result.toString()); + +// order of operations: subtract / reject +var zdt = Temporal.ZonedDateTime.from("2020-03-31T00:00-08:00[-08:00]"); +var d = Temporal.Duration.from({ + months: 1, + days: 1 +}); +var options = { overflow: "reject" }; +assert.throws(RangeError, () => zdt.subtract(d, options)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/property-bags.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/property-bags.js new file mode 100644 index 0000000000..e308c33e23 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/property-bags.js @@ -0,0 +1,296 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: property bags +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var lagos = Temporal.TimeZone.from("+01:00"); + +// can be constructed with monthCode and without month +assert.sameValue(`${ Temporal.ZonedDateTime.from({ + year: 1976, + monthCode: "M11", + day: 18, + timeZone: lagos +}) }`, "1976-11-18T00:00:00+01:00[+01:00]"); + +// can be constructed with month and without monthCode +assert.sameValue(`${ Temporal.ZonedDateTime.from({ + year: 1976, + month: 11, + day: 18, + timeZone: lagos +}) }`, "1976-11-18T00:00:00+01:00[+01:00]"); + +// month and monthCode must agree +assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ + year: 1976, + month: 11, + monthCode: "M12", + day: 18, + timeZone: lagos +})); + +// Temporal.ZonedDateTime.from({}) throws +assert.throws(TypeError, () => Temporal.ZonedDateTime.from({})) + +// Temporal.ZonedDateTime.from(required prop undefined) throws +assert.throws(TypeError, () => Temporal.ZonedDateTime.from({ + year: 1976, + month: undefined, + monthCode: undefined, + day: 18, + timeZone: lagos +})) + +// options may be a function object +assert.sameValue(`${ Temporal.ZonedDateTime.from({ + year: 1976, + month: 11, + day: 18, + timeZone: lagos +}, () => { +}) }`, "1976-11-18T00:00:00+01:00[+01:00]"); + +// object must contain at least the required correctly-spelled properties +assert.throws(TypeError, () => Temporal.ZonedDateTime.from({ + years: 1976, + months: 11, + days: 18, + timeZone: lagos +})); + +// incorrectly-spelled properties are ignored +assert.sameValue(`${ Temporal.ZonedDateTime.from({ + year: 1976, + month: 11, + day: 18, + timeZone: lagos, + hours: 12 +}) }`, "1976-11-18T00:00:00+01:00[+01:00]"); + +// does not accept non-string offset property +[ + null, + true, + 1000, + 1000n, + Symbol(), + {} +].forEach(offset => { + assert.throws( + typeof offset === "string" || (typeof offset === "object" && offset !== null) || typeof offset === "function" + ? RangeError + : TypeError, + () => Temporal.ZonedDateTime.from({ + year: 1976, + month: 11, + day: 18, + offset: offset, + timeZone: Temporal.TimeZone.from("+10:00") + }) + ) +}); + + +// overflow options +var bad = { + year: 2019, + month: 1, + day: 32, + timeZone: lagos +}; +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(bad, { overflow: "reject" })); +assert.sameValue(`${ Temporal.ZonedDateTime.from(bad) }`, "2019-01-31T00:00:00+01:00[+01:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(bad, { overflow: "constrain" }) }`, "2019-01-31T00:00:00+01:00[+01:00]"); + +// Offset options + +// { offset: 'reject' } throws if offset does not match offset time zone +var obj = { + year: 2020, + month: 3, + day: 8, + hour: 1, + offset: "-04:00", + timeZone: "-08:00" +}; +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj)); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj, { offset: "reject" })); + +// { offset: 'reject' } throws if offset does not match IANA time zone +var obj = { + year: 2020, + month: 3, + day: 8, + hour: 1, + offset: "-04:00", + timeZone: "UTC" +}; +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj)); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj, { offset: "reject" })); + +var cali = TemporalHelpers.springForwardFallBackTimeZone(); +var date = { +year: 2000, +month: 10, +day: 29, +timeZone: cali +}; +// { offset: 'prefer' } if offset matches time zone (first 1:30 when DST ends) +var obj = { + ...date, + hour: 1, + minute: 30, + offset: "-07:00" +}; +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { offset: "prefer" }) }`, "2000-10-29T01:30:00-07:00[Custom/Spring_Fall]"); + +// { offset: 'prefer' } if offset matches time zone (second 1:30 when DST ends) +var obj = { + ...date, + hour: 1, + minute: 30, + offset: "-08:00" +}; +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { offset: "prefer" }) }`, "2000-10-29T01:30:00-08:00[Custom/Spring_Fall]"); + +// { offset: 'prefer' } if offset does not match time zone" +var obj = { + ...date, + hour: 4, + offset: "-07:00" +}; +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { offset: "prefer" }) }`, "2000-10-29T04:00:00-08:00[Custom/Spring_Fall]"); + +// { offset: 'ignore' } uses time zone only +var obj = { + ...date, + hour: 4, + offset: "-12:00" +}; +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { offset: "ignore" }) }`, "2000-10-29T04:00:00-08:00[Custom/Spring_Fall]"); + +// { offset: 'use' } uses offset only +var obj = { + ...date, + hour: 4, + offset: "-07:00" +}; +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { offset: "use" }) }`, "2000-10-29T03:00:00-08:00[Custom/Spring_Fall]"); + +// Disambiguation options + +// plain datetime with multiple instants - Fall DST +var obj = { + year: 2000, + month: 10, + day: 29, + hour: 1, + minute: 45, + timeZone: cali +}; +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj) }`, "2000-10-29T01:45:00-07:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { disambiguation: "compatible" }) }`, "2000-10-29T01:45:00-07:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { disambiguation: "earlier" }) }`, "2000-10-29T01:45:00-07:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { disambiguation: "later" }) }`, "2000-10-29T01:45:00-08:00[Custom/Spring_Fall]"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj, { disambiguation: "reject" })); + +// plain datetime with multiple instants - Spring DST +var obj = { + year: 2000, + month: 4, + day: 2, + hour: 2, + minute: 30, + timeZone: cali +}; +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { disambiguation: "compatible" }) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { disambiguation: "earlier" }) }`, "2000-04-02T01:30:00-08:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { disambiguation: "later" }) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj, { disambiguation: "reject" })); + +// uses disambiguation if offset is ignored +var obj = { + year: 2000, + month: 4, + day: 2, + hour: 2, + minute: 30, + timeZone: cali +}; +var offset = "ignore"; +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { offset }) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { + offset, + disambiguation: "compatible" +}) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { + offset, + disambiguation: "earlier" +}) }`, "2000-04-02T01:30:00-08:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { + offset, + disambiguation: "later" +}) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj, { disambiguation: "reject" })); + +// uses disambiguation if offset is wrong and option is prefer +var obj = { + year: 2000, + month: 4, + day: 2, + hour: 2, + minute: 30, + offset: "-23:59", + timeZone: cali +}; +var offset = "prefer"; +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { offset }) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { + offset, + disambiguation: "compatible" +}) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { + offset, + disambiguation: "earlier" +}) }`, "2000-04-02T01:30:00-08:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { + offset, + disambiguation: "later" +}) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj, { + offset, + disambiguation: "reject" +})); + +// throw when bad disambiguation +[ + "", + "EARLIER", + "balance", + 3, + null +].forEach(disambiguation => { + assert.throws(RangeError, () => Temporal.ZonedDateTime.from("2020-11-01T04:00[UTC]", { disambiguation })); +}); + +// sub-minute time zone offsets + +// does not truncate offset property to minutes +var zdt = Temporal.ZonedDateTime.from({ + year: 1971, + month: 1, + day: 1, + hour: 12, + timeZone: TemporalHelpers.specificOffsetTimeZone(-2.67e12) // -00:44:30 in nanoseconds +}); +assert.sameValue(zdt.offset, "-00:44:30"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/reversibility-of-differences.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/reversibility-of-differences.js new file mode 100644 index 0000000000..909d7b6b61 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/reversibility-of-differences.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Reversibility of differences +features: [Temporal] +---*/ + +var earlier = Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456789-03:00[-03:00]"); +var later = Temporal.ZonedDateTime.from("2019-10-29T10:46:38.271986102-03:00[-03:00]"); +[ + "hours", + "minutes", + "seconds" +].forEach(largestUnit => { + var diff = later.since(earlier, { largestUnit }); +assert.sameValue(`${ earlier.since(later, { largestUnit }) }`, `${ diff.negated() }`); +assert.sameValue(`${ earlier.until(later, { largestUnit }) }`, `${ diff }`); +// difference symmetrical with regard to negative durations + assert(earlier.subtract(diff.negated()).equals(later)); + assert(later.add(diff.negated()).equals(earlier)); + }); +[ + "years", + "months", + "weeks", + "days", + "hours", + "minutes", + "seconds" +].forEach(largestUnit => { + var diff1 = earlier.until(later, { largestUnit }); + var diff2 = later.since(earlier, { largestUnit }); + assert(earlier.add(diff1).equals(later)); + assert(later.subtract(diff2).equals(earlier)); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/round.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/round.js new file mode 100644 index 0000000000..3dfb21989d --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/round.js @@ -0,0 +1,242 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.round() +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var zdt = Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456789+01:00[+01:00]"); + +// throws without parameter +assert.throws(TypeError, () => zdt.round()); + +// throws without required smallestUnit parameter +assert.throws(RangeError, () => zdt.round({})); +assert.throws(RangeError, () => zdt.round({ + roundingIncrement: 1, + roundingMode: "ceil" +})); + +// throws on disallowed or invalid smallestUnit (string param) +[ + "era", + "year", + "month", + "week", + "years", + "months", + "weeks", + "nonsense" +].forEach(smallestUnit => { + assert.throws(RangeError, () => zdt.round(smallestUnit)); +}); + +// rounds to an increment of hours +assert.sameValue(`${ zdt.round({ + smallestUnit: "hour", + roundingIncrement: 4 +}) }`, "1976-11-18T16:00:00+01:00[+01:00]"); + +// rounds to an increment of minutes +assert.sameValue(`${ zdt.round({ + smallestUnit: "minute", + roundingIncrement: 15 +}) }`, "1976-11-18T15:30:00+01:00[+01:00]"); + +// rounds to an increment of seconds +assert.sameValue(`${ zdt.round({ + smallestUnit: "second", + roundingIncrement: 30 +}) }`, "1976-11-18T15:23:30+01:00[+01:00]"); + +// rounds to an increment of milliseconds +assert.sameValue(`${ zdt.round({ + smallestUnit: "millisecond", + roundingIncrement: 10 +}) }`, "1976-11-18T15:23:30.12+01:00[+01:00]"); + +// rounds to an increment of microseconds +assert.sameValue(`${ zdt.round({ + smallestUnit: "microsecond", + roundingIncrement: 10 +}) }`, "1976-11-18T15:23:30.12346+01:00[+01:00]"); + +// rounds to an increment of nanoseconds +assert.sameValue(`${ zdt.round({ + smallestUnit: "nanosecond", + roundingIncrement: 10 +}) }`, "1976-11-18T15:23:30.12345679+01:00[+01:00]"); + +// 1 day is a valid increment +assert.sameValue(`${ zdt.round({ + smallestUnit: "day", + roundingIncrement: 1 +}) }`, "1976-11-19T00:00:00+01:00[+01:00]"); + +// valid hour increments divide into 24 +var smallestUnit = "hour"; +[ + 1, + 2, + 3, + 4, + 6, + 8, + 12 +].forEach(roundingIncrement => { + assert(zdt.round({ + smallestUnit, + roundingIncrement + }) instanceof Temporal.ZonedDateTime); +}); +[ + "minute", + "second" +].forEach(smallestUnit => { + // valid minutes/seconds increments divide into 60`, () => { + [ + 1, + 2, + 3, + 4, + 5, + 6, + 10, + 12, + 15, + 20, + 30 + ].forEach(roundingIncrement => { + assert(zdt.round({ + smallestUnit, + roundingIncrement + }) instanceof Temporal.ZonedDateTime); + }); + }); +[ + "millisecond", + "microsecond", + "nanosecond" +].forEach(smallestUnit => { + // valid increments divide into 1000` + [ + 1, + 2, + 4, + 5, + 8, + 10, + 20, + 25, + 40, + 50, + 100, + 125, + 200, + 250, + 500 + ].forEach(roundingIncrement => { + assert(zdt.round({ + smallestUnit, + roundingIncrement + }) instanceof Temporal.ZonedDateTime); + }); + }); + +// throws on increments that do not divide evenly into the next highest +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "day", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "hour", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "minute", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "second", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "millisecond", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "microsecond", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "nanosecond", + roundingIncrement: 29 +})); + +// throws on increments that are equal to the next highest +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "hour", + roundingIncrement: 24 +})); +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "minute", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "second", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "millisecond", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "microsecond", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "nanosecond", + roundingIncrement: 1000 +})); +var bal = Temporal.ZonedDateTime.from("1976-11-18T23:59:59.999999999+01:00[+01:00]"); +[ + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond" +].forEach(smallestUnit => { + assert.sameValue(`${ bal.round({ smallestUnit }) }`, "1976-11-19T00:00:00+01:00[+01:00]"); +}); + +var timeZone = TemporalHelpers.springForwardFallBackTimeZone(); + +// rounds correctly to a 25-hour day +var roundTo = { smallestUnit: "day" }; +var roundMeDown = Temporal.PlainDateTime.from("2000-10-29T12:29:59").toZonedDateTime(timeZone); +assert.sameValue(`${ roundMeDown.round(roundTo) }`, "2000-10-29T00:00:00-07:00[Custom/Spring_Fall]"); +var roundMeUp = Temporal.PlainDateTime.from("2000-10-29T12:30:01").toZonedDateTime(timeZone); +assert.sameValue(`${ roundMeUp.round(roundTo) }`, "2000-10-30T00:00:00-08:00[Custom/Spring_Fall]"); + +// rounds correctly to a 23-hour day +var roundTo = { smallestUnit: "day" }; +var roundMeDown = Temporal.PlainDateTime.from("2000-04-02T11:29:59").toZonedDateTime(timeZone); +assert.sameValue(`${ roundMeDown.round(roundTo) }`, "2000-04-02T00:00:00-08:00[Custom/Spring_Fall]"); +var roundMeUp = Temporal.PlainDateTime.from("2000-04-02T11:30:01").toZonedDateTime(timeZone); +assert.sameValue(`${ roundMeUp.round(roundTo) }`, "2000-04-03T00:00:00-07:00[Custom/Spring_Fall]"); + +// rounding up to a nonexistent wall-clock time +var almostSkipped = Temporal.PlainDateTime.from("2000-04-02T01:59:59.999999999").toZonedDateTime(timeZone); +var rounded = almostSkipped.round({ + smallestUnit: "microsecond", + roundingMode: "halfExpand" +}); +assert.sameValue(`${ rounded }`, "2000-04-02T03:00:00-07:00[Custom/Spring_Fall]"); +assert.sameValue(rounded.epochNanoseconds - almostSkipped.epochNanoseconds, 1n); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/shell.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/shell.js new file mode 100644 index 0000000000..60f74c2518 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/shell.js @@ -0,0 +1,2158 @@ +// GENERATED, DO NOT EDIT +// file: temporalHelpers.js +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: | + This defines helper objects and functions for testing Temporal. +defines: [TemporalHelpers] +features: [Symbol.species, Symbol.iterator, Temporal] +---*/ + +const ASCII_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/u; + +function formatPropertyName(propertyKey, objectName = "") { + switch (typeof propertyKey) { + case "symbol": + if (Symbol.keyFor(propertyKey) !== undefined) { + return `${objectName}[Symbol.for('${Symbol.keyFor(propertyKey)}')]`; + } else if (propertyKey.description.startsWith('Symbol.')) { + return `${objectName}[${propertyKey.description}]`; + } else { + return `${objectName}[Symbol('${propertyKey.description}')]` + } + case "string": + if (propertyKey !== String(Number(propertyKey))) { + if (ASCII_IDENTIFIER.test(propertyKey)) { + return objectName ? `${objectName}.${propertyKey}` : propertyKey; + } + return `${objectName}['${propertyKey.replace(/'/g, "\\'")}']` + } + // fall through + default: + // integer or string integer-index + return `${objectName}[${propertyKey}]`; + } +} + +const SKIP_SYMBOL = Symbol("Skip"); + +var TemporalHelpers = { + /* + * Codes and maximum lengths of months in the ISO 8601 calendar. + */ + ISOMonths: [ + { month: 1, monthCode: "M01", daysInMonth: 31 }, + { month: 2, monthCode: "M02", daysInMonth: 29 }, + { month: 3, monthCode: "M03", daysInMonth: 31 }, + { month: 4, monthCode: "M04", daysInMonth: 30 }, + { month: 5, monthCode: "M05", daysInMonth: 31 }, + { month: 6, monthCode: "M06", daysInMonth: 30 }, + { month: 7, monthCode: "M07", daysInMonth: 31 }, + { month: 8, monthCode: "M08", daysInMonth: 31 }, + { month: 9, monthCode: "M09", daysInMonth: 30 }, + { month: 10, monthCode: "M10", daysInMonth: 31 }, + { month: 11, monthCode: "M11", daysInMonth: 30 }, + { month: 12, monthCode: "M12", daysInMonth: 31 } + ], + + /* + * assertDuration(duration, years, ..., nanoseconds[, description]): + * + * Shorthand for asserting that each field of a Temporal.Duration is equal to + * an expected value. + */ + assertDuration(duration, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(duration instanceof Temporal.Duration, `${prefix}instanceof`); + assert.sameValue(duration.years, years, `${prefix}years result:`); + assert.sameValue(duration.months, months, `${prefix}months result:`); + assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`); + assert.sameValue(duration.days, days, `${prefix}days result:`); + assert.sameValue(duration.hours, hours, `${prefix}hours result:`); + assert.sameValue(duration.minutes, minutes, `${prefix}minutes result:`); + assert.sameValue(duration.seconds, seconds, `${prefix}seconds result:`); + assert.sameValue(duration.milliseconds, milliseconds, `${prefix}milliseconds result:`); + assert.sameValue(duration.microseconds, microseconds, `${prefix}microseconds result:`); + assert.sameValue(duration.nanoseconds, nanoseconds, `${prefix}nanoseconds result`); + }, + + /* + * assertDateDuration(duration, years, months, weeks, days, [, description]): + * + * Shorthand for asserting that each date field of a Temporal.Duration is + * equal to an expected value. + */ + assertDateDuration(duration, years, months, weeks, days, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(duration instanceof Temporal.Duration, `${prefix}instanceof`); + assert.sameValue(duration.years, years, `${prefix}years result:`); + assert.sameValue(duration.months, months, `${prefix}months result:`); + assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`); + assert.sameValue(duration.days, days, `${prefix}days result:`); + assert.sameValue(duration.hours, 0, `${prefix}hours result should be zero:`); + assert.sameValue(duration.minutes, 0, `${prefix}minutes result should be zero:`); + assert.sameValue(duration.seconds, 0, `${prefix}seconds result should be zero:`); + assert.sameValue(duration.milliseconds, 0, `${prefix}milliseconds result should be zero:`); + assert.sameValue(duration.microseconds, 0, `${prefix}microseconds result should be zero:`); + assert.sameValue(duration.nanoseconds, 0, `${prefix}nanoseconds result should be zero:`); + }, + + /* + * assertDurationsEqual(actual, expected[, description]): + * + * Shorthand for asserting that each field of a Temporal.Duration is equal to + * the corresponding field in another Temporal.Duration. + */ + assertDurationsEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.Duration, `${prefix}expected value should be a Temporal.Duration`); + TemporalHelpers.assertDuration(actual, expected.years, expected.months, expected.weeks, expected.days, expected.hours, expected.minutes, expected.seconds, expected.milliseconds, expected.microseconds, expected.nanoseconds, description); + }, + + /* + * assertInstantsEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.Instants are of the correct type + * and equal according to their equals() methods. + */ + assertInstantsEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.Instant, `${prefix}expected value should be a Temporal.Instant`); + assert(actual instanceof Temporal.Instant, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + }, + + /* + * assertPlainDate(date, year, ..., nanosecond[, description[, era, eraYear]]): + * + * Shorthand for asserting that each field of a Temporal.PlainDate is equal to + * an expected value. (Except the `calendar` property, since callers may want + * to assert either object equality with an object they put in there, or the + * value of date.calendarId.) + */ + assertPlainDate(date, year, month, monthCode, day, description = "", era = undefined, eraYear = undefined) { + const prefix = description ? `${description}: ` : ""; + assert(date instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert.sameValue(date.era, era, `${prefix}era result:`); + assert.sameValue(date.eraYear, eraYear, `${prefix}eraYear result:`); + assert.sameValue(date.year, year, `${prefix}year result:`); + assert.sameValue(date.month, month, `${prefix}month result:`); + assert.sameValue(date.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(date.day, day, `${prefix}day result:`); + }, + + /* + * assertPlainDateTime(datetime, year, ..., nanosecond[, description[, era, eraYear]]): + * + * Shorthand for asserting that each field of a Temporal.PlainDateTime is + * equal to an expected value. (Except the `calendar` property, since callers + * may want to assert either object equality with an object they put in there, + * or the value of datetime.calendarId.) + */ + assertPlainDateTime(datetime, year, month, monthCode, day, hour, minute, second, millisecond, microsecond, nanosecond, description = "", era = undefined, eraYear = undefined) { + const prefix = description ? `${description}: ` : ""; + assert(datetime instanceof Temporal.PlainDateTime, `${prefix}instanceof`); + assert.sameValue(datetime.era, era, `${prefix}era result:`); + assert.sameValue(datetime.eraYear, eraYear, `${prefix}eraYear result:`); + assert.sameValue(datetime.year, year, `${prefix}year result:`); + assert.sameValue(datetime.month, month, `${prefix}month result:`); + assert.sameValue(datetime.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(datetime.day, day, `${prefix}day result:`); + assert.sameValue(datetime.hour, hour, `${prefix}hour result:`); + assert.sameValue(datetime.minute, minute, `${prefix}minute result:`); + assert.sameValue(datetime.second, second, `${prefix}second result:`); + assert.sameValue(datetime.millisecond, millisecond, `${prefix}millisecond result:`); + assert.sameValue(datetime.microsecond, microsecond, `${prefix}microsecond result:`); + assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); + }, + + /* + * assertPlainDateTimesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDateTimes are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDateTimesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDateTime, `${prefix}expected value should be a Temporal.PlainDateTime`); + assert(actual instanceof Temporal.PlainDateTime, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.getISOFields().calendar, + expected.getISOFields().calendar, + `${prefix}calendar same value:` + ); + }, + + /* + * assertPlainMonthDay(monthDay, monthCode, day[, description [, referenceISOYear]]): + * + * Shorthand for asserting that each field of a Temporal.PlainMonthDay is + * equal to an expected value. (Except the `calendar` property, since callers + * may want to assert either object equality with an object they put in there, + * or the value of monthDay.calendarId().) + */ + assertPlainMonthDay(monthDay, monthCode, day, description = "", referenceISOYear = 1972) { + const prefix = description ? `${description}: ` : ""; + assert(monthDay instanceof Temporal.PlainMonthDay, `${prefix}instanceof`); + assert.sameValue(monthDay.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(monthDay.day, day, `${prefix}day result:`); + assert.sameValue(monthDay.getISOFields().isoYear, referenceISOYear, `${prefix}referenceISOYear result:`); + }, + + /* + * assertPlainTime(time, hour, ..., nanosecond[, description]): + * + * Shorthand for asserting that each field of a Temporal.PlainTime is equal to + * an expected value. + */ + assertPlainTime(time, hour, minute, second, millisecond, microsecond, nanosecond, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(time instanceof Temporal.PlainTime, `${prefix}instanceof`); + assert.sameValue(time.hour, hour, `${prefix}hour result:`); + assert.sameValue(time.minute, minute, `${prefix}minute result:`); + assert.sameValue(time.second, second, `${prefix}second result:`); + assert.sameValue(time.millisecond, millisecond, `${prefix}millisecond result:`); + assert.sameValue(time.microsecond, microsecond, `${prefix}microsecond result:`); + assert.sameValue(time.nanosecond, nanosecond, `${prefix}nanosecond result:`); + }, + + /* + * assertPlainTimesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainTimes are of the correct + * type and equal according to their equals() methods. + */ + assertPlainTimesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainTime, `${prefix}expected value should be a Temporal.PlainTime`); + assert(actual instanceof Temporal.PlainTime, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + }, + + /* + * assertPlainYearMonth(yearMonth, year, month, monthCode[, description[, era, eraYear, referenceISODay]]): + * + * Shorthand for asserting that each field of a Temporal.PlainYearMonth is + * equal to an expected value. (Except the `calendar` property, since callers + * may want to assert either object equality with an object they put in there, + * or the value of yearMonth.calendarId.) + */ + assertPlainYearMonth(yearMonth, year, month, monthCode, description = "", era = undefined, eraYear = undefined, referenceISODay = 1) { + const prefix = description ? `${description}: ` : ""; + assert(yearMonth instanceof Temporal.PlainYearMonth, `${prefix}instanceof`); + assert.sameValue(yearMonth.era, era, `${prefix}era result:`); + assert.sameValue(yearMonth.eraYear, eraYear, `${prefix}eraYear result:`); + assert.sameValue(yearMonth.year, year, `${prefix}year result:`); + assert.sameValue(yearMonth.month, month, `${prefix}month result:`); + assert.sameValue(yearMonth.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(yearMonth.getISOFields().isoDay, referenceISODay, `${prefix}referenceISODay result:`); + }, + + /* + * assertZonedDateTimesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.ZonedDateTimes are of the correct + * type, equal according to their equals() methods, and additionally that + * their time zones and calendar internal slots are the same value. + */ + assertZonedDateTimesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.ZonedDateTime, `${prefix}expected value should be a Temporal.ZonedDateTime`); + assert(actual instanceof Temporal.ZonedDateTime, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue(actual.timeZone, expected.timeZone, `${prefix}time zone same value:`); + assert.sameValue( + actual.getISOFields().calendar, + expected.getISOFields().calendar, + `${prefix}calendar same value:` + ); + }, + + /* + * assertUnreachable(description): + * + * Helper for asserting that code is not executed. This is useful for + * assertions that methods of user calendars and time zones are not called. + */ + assertUnreachable(description) { + let message = "This code should not be executed"; + if (description) { + message = `${message}: ${description}`; + } + throw new Test262Error(message); + }, + + /* + * checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls): + * + * When an options object with a largestUnit property is synthesized inside + * Temporal and passed to user code such as calendar.dateUntil(), the value of + * the largestUnit property should be in the singular form, even if the input + * was given in the plural form. + * (This doesn't apply when the options object is passed through verbatim.) + * + * func(calendar, largestUnit, index) is the operation under test. It's called + * with an instance of a calendar that keeps track of which largestUnit is + * passed to dateUntil(), each key of expectedLargestUnitCalls in turn, and + * the key's numerical index in case the function needs to generate test data + * based on the index. At the end, the actual values passed to dateUntil() are + * compared with the array values of expectedLargestUnitCalls. + */ + checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls) { + const actual = []; + + class DateUntilOptionsCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateUntil(earlier, later, options) { + actual.push(options.largestUnit); + return super.dateUntil(earlier, later, options); + } + + toString() { + return "date-until-options"; + } + } + + const calendar = new DateUntilOptionsCalendar(); + Object.entries(expectedLargestUnitCalls).forEach(([largestUnit, expected], index) => { + func(calendar, largestUnit, index); + assert.compareArray(actual, expected, `largestUnit passed to calendar.dateUntil() for largestUnit ${largestUnit}`); + actual.splice(0); // empty it for the next check + }); + }, + + /* + * checkPlainDateTimeConversionFastPath(func): + * + * ToTemporalDate and ToTemporalTime should both, if given a + * Temporal.PlainDateTime instance, convert to the desired type by reading the + * PlainDateTime's internal slots, rather than calling any getters. + * + * func(datetime, calendar) is the actual operation to test, that must + * internally call the abstract operation ToTemporalDate or ToTemporalTime. + * It is passed a Temporal.PlainDateTime instance, as well as the instance's + * calendar object (so that it doesn't have to call the calendar getter itself + * if it wants to make any assertions about the calendar.) + */ + checkPlainDateTimeConversionFastPath(func, message = "checkPlainDateTimeConversionFastPath") { + const actual = []; + const expected = []; + + const calendar = new Temporal.Calendar("iso8601"); + const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar); + const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDateTime.prototype); + ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => { + Object.defineProperty(datetime, property, { + get() { + actual.push(`get ${formatPropertyName(property)}`); + const value = prototypeDescrs[property].get.call(this); + return { + toString() { + actual.push(`toString ${formatPropertyName(property)}`); + return value.toString(); + }, + valueOf() { + actual.push(`valueOf ${formatPropertyName(property)}`); + return value; + }, + }; + }, + }); + }); + Object.defineProperty(datetime, "calendar", { + get() { + actual.push("get calendar"); + return calendar; + }, + }); + + func(datetime, calendar); + assert.compareArray(actual, expected, `${message}: property getters not called`); + }, + + /* + * Check that an options bag that accepts units written in the singular form, + * also accepts the same units written in the plural form. + * func(unit) should call the method with the appropriate options bag + * containing unit as a value. This will be called twice for each element of + * validSingularUnits, once with singular and once with plural, and the + * results of each pair should be the same (whether a Temporal object or a + * primitive value.) + */ + checkPluralUnitsAccepted(func, validSingularUnits) { + const plurals = { + year: 'years', + month: 'months', + week: 'weeks', + day: 'days', + hour: 'hours', + minute: 'minutes', + second: 'seconds', + millisecond: 'milliseconds', + microsecond: 'microseconds', + nanosecond: 'nanoseconds', + }; + + validSingularUnits.forEach((unit) => { + const singularValue = func(unit); + const pluralValue = func(plurals[unit]); + const desc = `Plural ${plurals[unit]} produces the same result as singular ${unit}`; + if (singularValue instanceof Temporal.Duration) { + TemporalHelpers.assertDurationsEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.Instant) { + TemporalHelpers.assertInstantsEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.PlainDateTime) { + TemporalHelpers.assertPlainDateTimesEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.PlainTime) { + TemporalHelpers.assertPlainTimesEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.ZonedDateTime) { + TemporalHelpers.assertZonedDateTimesEqual(pluralValue, singularValue, desc); + } else { + assert.sameValue(pluralValue, singularValue); + } + }); + }, + + /* + * checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc): + * + * Checks the type handling of the roundingIncrement option. + * checkFunc(roundingIncrement) is a function which takes the value of + * roundingIncrement to test, and calls the method under test with it, + * returning the result. assertTrueResultFunc(result, description) should + * assert that result is the expected result with roundingIncrement: true, and + * assertObjectResultFunc(result, description) should assert that result is + * the expected result with roundingIncrement being an object with a valueOf() + * method. + */ + checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc) { + // null converts to 0, which is out of range + assert.throws(RangeError, () => checkFunc(null), "null"); + // Booleans convert to either 0 or 1, and 1 is allowed + const trueResult = checkFunc(true); + assertTrueResultFunc(trueResult, "true"); + assert.throws(RangeError, () => checkFunc(false), "false"); + // Symbols and BigInts cannot convert to numbers + assert.throws(TypeError, () => checkFunc(Symbol()), "symbol"); + assert.throws(TypeError, () => checkFunc(2n), "bigint"); + + // Objects prefer their valueOf() methods when converting to a number + assert.throws(RangeError, () => checkFunc({}), "plain object"); + + const expected = [ + "get roundingIncrement.valueOf", + "call roundingIncrement.valueOf", + ]; + const actual = []; + const observer = TemporalHelpers.toPrimitiveObserver(actual, 2, "roundingIncrement"); + const objectResult = checkFunc(observer); + assertObjectResultFunc(objectResult, "object with valueOf"); + assert.compareArray(actual, expected, "order of operations"); + }, + + /* + * checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc): + * + * Checks the type handling of a string option, of which there are several in + * Temporal. + * propertyName is the name of the option, and value is the value that + * assertFunc should expect it to have. + * checkFunc(value) is a function which takes the value of the option to test, + * and calls the method under test with it, returning the result. + * assertFunc(result, description) should assert that result is the expected + * result with the option value being an object with a toString() method + * which returns the given value. + */ + checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc) { + // null converts to the string "null", which is an invalid string value + assert.throws(RangeError, () => checkFunc(null), "null"); + // Booleans convert to the strings "true" or "false", which are invalid + assert.throws(RangeError, () => checkFunc(true), "true"); + assert.throws(RangeError, () => checkFunc(false), "false"); + // Symbols cannot convert to strings + assert.throws(TypeError, () => checkFunc(Symbol()), "symbol"); + // Numbers convert to strings which are invalid + assert.throws(RangeError, () => checkFunc(2), "number"); + // BigInts convert to strings which are invalid + assert.throws(RangeError, () => checkFunc(2n), "bigint"); + + // Objects prefer their toString() methods when converting to a string + assert.throws(RangeError, () => checkFunc({}), "plain object"); + + const expected = [ + `get ${propertyName}.toString`, + `call ${propertyName}.toString`, + ]; + const actual = []; + const observer = TemporalHelpers.toPrimitiveObserver(actual, value, propertyName); + const result = checkFunc(observer); + assertFunc(result, "object with toString"); + assert.compareArray(actual, expected, "order of operations"); + }, + + /* + * checkSubclassingIgnored(construct, constructArgs, method, methodArgs, + * resultAssertions): + * + * Methods of Temporal classes that return a new instance of the same class, + * must not take the constructor of a subclass into account, nor the @@species + * property. This helper runs tests to ensure this. + * + * construct(...constructArgs) must yield a valid instance of the Temporal + * class. instance[method](...methodArgs) is the method call under test, which + * must also yield a valid instance of the same Temporal class, not a + * subclass. See below for the individual tests that this runs. + * resultAssertions() is a function that performs additional assertions on the + * instance returned by the method under test. + */ + checkSubclassingIgnored(...args) { + this.checkSubclassConstructorNotObject(...args); + this.checkSubclassConstructorUndefined(...args); + this.checkSubclassConstructorThrows(...args); + this.checkSubclassConstructorNotCalled(...args); + this.checkSubclassSpeciesInvalidResult(...args); + this.checkSubclassSpeciesNotAConstructor(...args); + this.checkSubclassSpeciesNull(...args); + this.checkSubclassSpeciesUndefined(...args); + this.checkSubclassSpeciesThrows(...args); + }, + + /* + * Checks that replacing the 'constructor' property of the instance with + * various primitive values does not affect the returned new instance. + */ + checkSubclassConstructorNotObject(construct, constructArgs, method, methodArgs, resultAssertions) { + function check(value, description) { + const instance = new construct(...constructArgs); + instance.constructor = value; + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); + resultAssertions(result); + } + + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "Symbol"); + check(7, "number"); + check(7n, "bigint"); + }, + + /* + * Checks that replacing the 'constructor' property of the subclass with + * undefined does not affect the returned new instance. + */ + checkSubclassConstructorUndefined(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + MySubclass.prototype.constructor = undefined; + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Checks that making the 'constructor' property of the instance throw when + * called does not affect the returned new instance. + */ + checkSubclassConstructorThrows(construct, constructArgs, method, methodArgs, resultAssertions) { + function CustomError() {} + const instance = new construct(...constructArgs); + Object.defineProperty(instance, "constructor", { + get() { + throw new CustomError(); + } + }); + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Checks that when subclassing, the subclass constructor is not called by + * the method under test. + */ + checkSubclassConstructorNotCalled(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that the constructor's @@species property is ignored when it's a + * constructor that returns a non-object value. + */ + checkSubclassSpeciesInvalidResult(construct, constructArgs, method, methodArgs, resultAssertions) { + function check(value, description) { + const instance = new construct(...constructArgs); + instance.constructor = { + [Symbol.species]: function() { + return value; + }, + }; + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); + resultAssertions(result); + } + + check(undefined, "undefined"); + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "Symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "plain object"); + }, + + /* + * Check that the constructor's @@species property is ignored when it's not a + * constructor. + */ + checkSubclassSpeciesNotAConstructor(construct, constructArgs, method, methodArgs, resultAssertions) { + function check(value, description) { + const instance = new construct(...constructArgs); + instance.constructor = { + [Symbol.species]: value, + }; + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); + resultAssertions(result); + } + + check(true, "true"); + check("test", "string"); + check(Symbol(), "Symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "plain object"); + }, + + /* + * Check that the constructor's @@species property is ignored when it's null. + */ + checkSubclassSpeciesNull(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + MySubclass.prototype.constructor = { + [Symbol.species]: null, + }; + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that the constructor's @@species property is ignored when it's + * undefined. + */ + checkSubclassSpeciesUndefined(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + MySubclass.prototype.constructor = { + [Symbol.species]: undefined, + }; + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that the constructor's @@species property is ignored when it throws, + * i.e. it is not called at all. + */ + checkSubclassSpeciesThrows(construct, constructArgs, method, methodArgs, resultAssertions) { + function CustomError() {} + + const instance = new construct(...constructArgs); + instance.constructor = { + get [Symbol.species]() { + throw new CustomError(); + }, + }; + + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + }, + + /* + * checkSubclassingIgnoredStatic(construct, method, methodArgs, resultAssertions): + * + * Static methods of Temporal classes that return a new instance of the class, + * must not use the this-value as a constructor. This helper runs tests to + * ensure this. + * + * construct[method](...methodArgs) is the static method call under test, and + * must yield a valid instance of the Temporal class, not a subclass. See + * below for the individual tests that this runs. + * resultAssertions() is a function that performs additional assertions on the + * instance returned by the method under test. + */ + checkSubclassingIgnoredStatic(...args) { + this.checkStaticInvalidReceiver(...args); + this.checkStaticReceiverNotCalled(...args); + this.checkThisValueNotCalled(...args); + }, + + /* + * Check that calling the static method with a receiver that's not callable, + * still calls the intrinsic constructor. + */ + checkStaticInvalidReceiver(construct, method, methodArgs, resultAssertions) { + function check(value, description) { + const result = construct[method].apply(value, methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + } + + check(undefined, "undefined"); + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "Non-callable object"); + }, + + /* + * Check that calling the static method with a receiver that returns a value + * that's not callable, still calls the intrinsic constructor. + */ + checkStaticReceiverNotCalled(construct, method, methodArgs, resultAssertions) { + function check(value, description) { + const receiver = function () { + return value; + }; + const result = construct[method].apply(receiver, methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + } + + check(undefined, "undefined"); + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "Non-callable object"); + }, + + /* + * Check that the receiver isn't called. + */ + checkThisValueNotCalled(construct, method, methodArgs, resultAssertions) { + let called = false; + + class MySubclass extends construct { + constructor(...args) { + called = true; + super(...args); + } + } + + const result = MySubclass[method](...methodArgs); + assert.sameValue(called, false); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that any iterable returned from a custom time zone's + * getPossibleInstantsFor() method is exhausted. + * The custom time zone object is passed in to func(). + * expected is an array of strings representing the expected calls to the + * getPossibleInstantsFor() method. The PlainDateTimes that it is called with, + * are compared (using their toString() results) with the array. + */ + checkTimeZonePossibleInstantsIterable(func, expected) { + // A custom time zone that returns an iterable instead of an array from its + // getPossibleInstantsFor() method, and for testing purposes skips + // 00:00-01:00 UTC on January 1, 2030, and repeats 00:00-01:00 UTC+1 on + // January 3, 2030. Otherwise identical to the UTC time zone. + class TimeZonePossibleInstantsIterable extends Temporal.TimeZone { + constructor() { + super("UTC"); + this.getPossibleInstantsForCallCount = 0; + this.getPossibleInstantsForCalledWith = []; + this.getPossibleInstantsForReturns = []; + this.iteratorExhausted = []; + } + + toString() { + return "Custom/Iterable"; + } + + getOffsetNanosecondsFor(instant) { + if (Temporal.Instant.compare(instant, "2030-01-01T00:00Z") >= 0 && + Temporal.Instant.compare(instant, "2030-01-03T01:00Z") < 0) { + return 3600_000_000_000; + } else { + return 0; + } + } + + getPossibleInstantsFor(dateTime) { + this.getPossibleInstantsForCallCount++; + this.getPossibleInstantsForCalledWith.push(dateTime); + + // Fake DST transition + let retval = super.getPossibleInstantsFor(dateTime); + if (dateTime.toPlainDate().equals("2030-01-01") && dateTime.hour === 0) { + retval = []; + } else if (dateTime.toPlainDate().equals("2030-01-03") && dateTime.hour === 0) { + retval.push(retval[0].subtract({ hours: 1 })); + } else if (dateTime.year === 2030 && dateTime.month === 1 && dateTime.day >= 1 && dateTime.day <= 2) { + retval[0] = retval[0].subtract({ hours: 1 }); + } + + this.getPossibleInstantsForReturns.push(retval); + this.iteratorExhausted.push(false); + return { + callIndex: this.getPossibleInstantsForCallCount - 1, + timeZone: this, + *[Symbol.iterator]() { + yield* this.timeZone.getPossibleInstantsForReturns[this.callIndex]; + this.timeZone.iteratorExhausted[this.callIndex] = true; + }, + }; + } + } + + const timeZone = new TimeZonePossibleInstantsIterable(); + func(timeZone); + + assert.sameValue(timeZone.getPossibleInstantsForCallCount, expected.length, "getPossibleInstantsFor() method called correct number of times"); + + for (let index = 0; index < expected.length; index++) { + assert.sameValue(timeZone.getPossibleInstantsForCalledWith[index].toString(), expected[index], "getPossibleInstantsFor() called with expected PlainDateTime"); + assert(timeZone.iteratorExhausted[index], "iterated through the whole iterable"); + } + }, + + /* + * Check that any calendar-carrying Temporal object has its [[Calendar]] + * internal slot read by ToTemporalCalendar, and does not fetch the calendar + * by calling getters. + * The custom calendar object is passed in to func() so that it can do its + * own additional assertions involving the calendar if necessary. (Sometimes + * there is nothing to assert as the calendar isn't stored anywhere that can + * be asserted about.) + */ + checkToTemporalCalendarFastPath(func) { + class CalendarFastPathCheck extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateFromFields(...args) { + return super.dateFromFields(...args).withCalendar(this); + } + + monthDayFromFields(...args) { + const { isoYear, isoMonth, isoDay } = super.monthDayFromFields(...args).getISOFields(); + return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); + } + + yearMonthFromFields(...args) { + const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields(...args).getISOFields(); + return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); + } + + toString() { + return "fast-path-check"; + } + } + const calendar = new CalendarFastPathCheck(); + + const plainDate = new Temporal.PlainDate(2000, 5, 2, calendar); + const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar); + const plainMonthDay = new Temporal.PlainMonthDay(5, 2, calendar); + const plainYearMonth = new Temporal.PlainYearMonth(2000, 5, calendar); + const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + + [plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((temporalObject) => { + const actual = []; + const expected = []; + + Object.defineProperty(temporalObject, "calendar", { + get() { + actual.push("get calendar"); + return calendar; + }, + }); + + func(temporalObject, calendar); + assert.compareArray(actual, expected, "calendar getter not called"); + }); + }, + + checkToTemporalInstantFastPath(func) { + const actual = []; + const expected = []; + + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + Object.defineProperty(datetime, 'toString', { + get() { + actual.push("get toString"); + return function (options) { + actual.push("call toString"); + return Temporal.ZonedDateTime.prototype.toString.call(this, options); + }; + }, + }); + + func(datetime); + assert.compareArray(actual, expected, "toString not called"); + }, + + checkToTemporalPlainDateTimeFastPath(func) { + const actual = []; + const expected = []; + + const calendar = new Temporal.Calendar("iso8601"); + const date = new Temporal.PlainDate(2000, 5, 2, calendar); + const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDate.prototype); + ["year", "month", "monthCode", "day"].forEach((property) => { + Object.defineProperty(date, property, { + get() { + actual.push(`get ${formatPropertyName(property)}`); + const value = prototypeDescrs[property].get.call(this); + return TemporalHelpers.toPrimitiveObserver(actual, value, property); + }, + }); + }); + ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => { + Object.defineProperty(date, property, { + get() { + actual.push(`get ${formatPropertyName(property)}`); + return undefined; + }, + }); + }); + Object.defineProperty(date, "calendar", { + get() { + actual.push("get calendar"); + return calendar; + }, + }); + + func(date, calendar); + assert.compareArray(actual, expected, "property getters not called"); + }, + + /* + * A custom calendar used in prototype pollution checks. Verifies that the + * fromFields methods are always called with a null-prototype fields object. + */ + calendarCheckFieldsPrototypePollution() { + class CalendarCheckFieldsPrototypePollution extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateFromFieldsCallCount = 0; + this.yearMonthFromFieldsCallCount = 0; + this.monthDayFromFieldsCallCount = 0; + } + + // toString must remain "iso8601", so that some methods don't throw due to + // incompatible calendars + + dateFromFields(fields, options = {}) { + this.dateFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "dateFromFields should be called with null-prototype fields object"); + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options = {}) { + this.yearMonthFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "yearMonthFromFields should be called with null-prototype fields object"); + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options = {}) { + this.monthDayFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "monthDayFromFields should be called with null-prototype fields object"); + return super.monthDayFromFields(fields, options); + } + } + + return new CalendarCheckFieldsPrototypePollution(); + }, + + /* + * A custom calendar used in prototype pollution checks. Verifies that the + * mergeFields() method is always called with null-prototype fields objects. + */ + calendarCheckMergeFieldsPrototypePollution() { + class CalendarCheckMergeFieldsPrototypePollution extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.mergeFieldsCallCount = 0; + } + + toString() { + return "merge-fields-null-proto"; + } + + mergeFields(fields, additionalFields) { + this.mergeFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "mergeFields should be called with null-prototype fields object (first argument)"); + assert.sameValue(Object.getPrototypeOf(additionalFields), null, "mergeFields should be called with null-prototype fields object (second argument)"); + return super.mergeFields(fields, additionalFields); + } + } + + return new CalendarCheckMergeFieldsPrototypePollution(); + }, + + /* + * A custom calendar used in prototype pollution checks. Verifies that methods + * are always called with a null-prototype options object. + */ + calendarCheckOptionsPrototypePollution() { + class CalendarCheckOptionsPrototypePollution extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.yearMonthFromFieldsCallCount = 0; + this.dateUntilCallCount = 0; + } + + toString() { + return "options-null-proto"; + } + + yearMonthFromFields(fields, options) { + this.yearMonthFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(options), null, "yearMonthFromFields should be called with null-prototype options"); + return super.yearMonthFromFields(fields, options); + } + + dateUntil(one, two, options) { + this.dateUntilCallCount++; + assert.sameValue(Object.getPrototypeOf(options), null, "dateUntil should be called with null-prototype options"); + return super.dateUntil(one, two, options); + } + } + + return new CalendarCheckOptionsPrototypePollution(); + }, + + /* + * A custom calendar that asserts its dateAdd() method is called with the + * options parameter having the value undefined. + */ + calendarDateAddUndefinedOptions() { + class CalendarDateAddUndefinedOptions extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateAddCallCount = 0; + } + + toString() { + return "dateadd-undef-options"; + } + + dateAdd(date, duration, options) { + this.dateAddCallCount++; + assert.sameValue(options, undefined, "dateAdd shouldn't be called with options"); + return super.dateAdd(date, duration, options); + } + } + return new CalendarDateAddUndefinedOptions(); + }, + + /* + * A custom calendar that asserts its dateAdd() method is called with a + * PlainDate instance. Optionally, it also asserts that the PlainDate instance + * is the specific object `this.specificPlainDate`, if it is set by the + * calling code. + */ + calendarDateAddPlainDateInstance() { + class CalendarDateAddPlainDateInstance extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateAddCallCount = 0; + this.specificPlainDate = undefined; + } + + toString() { + return "dateadd-plain-date-instance"; + } + + dateFromFields(...args) { + return super.dateFromFields(...args).withCalendar(this); + } + + dateAdd(date, duration, options) { + this.dateAddCallCount++; + assert(date instanceof Temporal.PlainDate, "dateAdd() should be called with a PlainDate instance"); + if (this.dateAddCallCount === 1 && this.specificPlainDate) { + assert.sameValue(date, this.specificPlainDate, `dateAdd() should be called first with the specific PlainDate instance ${this.specificPlainDate}`); + } + return super.dateAdd(date, duration, options).withCalendar(this); + } + } + return new CalendarDateAddPlainDateInstance(); + }, + + /* + * A custom calendar that returns an iterable instead of an array from its + * fields() method, otherwise identical to the ISO calendar. + */ + calendarFieldsIterable() { + class CalendarFieldsIterable extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.fieldsCallCount = 0; + this.fieldsCalledWith = []; + this.iteratorExhausted = []; + } + + toString() { + return "fields-iterable"; + } + + fields(fieldNames) { + this.fieldsCallCount++; + this.fieldsCalledWith.push(fieldNames.slice()); + this.iteratorExhausted.push(false); + return { + callIndex: this.fieldsCallCount - 1, + calendar: this, + *[Symbol.iterator]() { + yield* this.calendar.fieldsCalledWith[this.callIndex]; + this.calendar.iteratorExhausted[this.callIndex] = true; + }, + }; + } + } + return new CalendarFieldsIterable(); + }, + + /* + * A custom calendar that asserts its ...FromFields() methods are called with + * the options parameter having the value undefined. + */ + calendarFromFieldsUndefinedOptions() { + class CalendarFromFieldsUndefinedOptions extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateFromFieldsCallCount = 0; + this.monthDayFromFieldsCallCount = 0; + this.yearMonthFromFieldsCallCount = 0; + } + + toString() { + return "from-fields-undef-options"; + } + + dateFromFields(fields, options) { + this.dateFromFieldsCallCount++; + assert.sameValue(options, undefined, "dateFromFields shouldn't be called with options"); + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options) { + this.yearMonthFromFieldsCallCount++; + assert.sameValue(options, undefined, "yearMonthFromFields shouldn't be called with options"); + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options) { + this.monthDayFromFieldsCallCount++; + assert.sameValue(options, undefined, "monthDayFromFields shouldn't be called with options"); + return super.monthDayFromFields(fields, options); + } + } + return new CalendarFromFieldsUndefinedOptions(); + }, + + /* + * A custom calendar that modifies the fields object passed in to + * dateFromFields, sabotaging its time properties. + */ + calendarMakeInfinityTime() { + class CalendarMakeInfinityTime extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateFromFields(fields, options) { + const retval = super.dateFromFields(fields, options); + fields.hour = Infinity; + fields.minute = Infinity; + fields.second = Infinity; + fields.millisecond = Infinity; + fields.microsecond = Infinity; + fields.nanosecond = Infinity; + return retval; + } + } + return new CalendarMakeInfinityTime(); + }, + + /* + * A custom calendar that defines getters on the fields object passed into + * dateFromFields that throw, sabotaging its time properties. + */ + calendarMakeInvalidGettersTime() { + class CalendarMakeInvalidGettersTime extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateFromFields(fields, options) { + const retval = super.dateFromFields(fields, options); + const throwingDescriptor = { + get() { + throw new Test262Error("reading a sabotaged time field"); + }, + }; + Object.defineProperties(fields, { + hour: throwingDescriptor, + minute: throwingDescriptor, + second: throwingDescriptor, + millisecond: throwingDescriptor, + microsecond: throwingDescriptor, + nanosecond: throwingDescriptor, + }); + return retval; + } + } + return new CalendarMakeInvalidGettersTime(); + }, + + /* + * A custom calendar whose mergeFields() method returns a proxy object with + * all of its Get and HasProperty operations observable, as well as adding a + * "shouldNotBeCopied": true property. + */ + calendarMergeFieldsGetters() { + class CalendarMergeFieldsGetters extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.mergeFieldsReturnOperations = []; + } + + toString() { + return "merge-fields-getters"; + } + + dateFromFields(fields, options) { + assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied"); + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options) { + assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied"); + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options) { + assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied"); + return super.monthDayFromFields(fields, options); + } + + mergeFields(fields, additionalFields) { + const retval = super.mergeFields(fields, additionalFields); + retval._calendar = this; + retval.shouldNotBeCopied = true; + return new Proxy(retval, { + get(target, key) { + target._calendar.mergeFieldsReturnOperations.push(`get ${key}`); + const result = target[key]; + if (result === undefined) { + return undefined; + } + return TemporalHelpers.toPrimitiveObserver(target._calendar.mergeFieldsReturnOperations, result, key); + }, + has(target, key) { + target._calendar.mergeFieldsReturnOperations.push(`has ${key}`); + return key in target; + }, + }); + } + } + return new CalendarMergeFieldsGetters(); + }, + + /* + * A custom calendar whose mergeFields() method returns a primitive value, + * given by @primitive, and which records the number of calls made to its + * dateFromFields(), yearMonthFromFields(), and monthDayFromFields() methods. + */ + calendarMergeFieldsReturnsPrimitive(primitive) { + class CalendarMergeFieldsPrimitive extends Temporal.Calendar { + constructor(mergeFieldsReturnValue) { + super("iso8601"); + this._mergeFieldsReturnValue = mergeFieldsReturnValue; + this.dateFromFieldsCallCount = 0; + this.monthDayFromFieldsCallCount = 0; + this.yearMonthFromFieldsCallCount = 0; + } + + toString() { + return "merge-fields-primitive"; + } + + dateFromFields(fields, options) { + this.dateFromFieldsCallCount++; + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options) { + this.yearMonthFromFieldsCallCount++; + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options) { + this.monthDayFromFieldsCallCount++; + return super.monthDayFromFields(fields, options); + } + + mergeFields() { + return this._mergeFieldsReturnValue; + } + } + return new CalendarMergeFieldsPrimitive(primitive); + }, + + /* + * A custom calendar whose fields() method returns the same value as the + * iso8601 calendar, with the addition of extraFields provided as parameter. + */ + calendarWithExtraFields(fields) { + class CalendarWithExtraFields extends Temporal.Calendar { + constructor(extraFields) { + super("iso8601"); + this._extraFields = extraFields; + } + + fields(fieldNames) { + return super.fields(fieldNames).concat(this._extraFields); + } + } + + return new CalendarWithExtraFields(fields); + }, + + /* + * crossDateLineTimeZone(): + * + * This returns an instance of a custom time zone class that implements one + * single transition where the time zone moves from one side of the + * International Date Line to the other, for the purpose of testing time zone + * calculations without depending on system time zone data. + * + * The transition occurs at epoch second 1325239200 and goes from offset + * -10:00 to +14:00. In other words, the time zone skips the whole calendar + * day of 2011-12-30. This is the same as the real-life transition in the + * Pacific/Apia time zone. + */ + crossDateLineTimeZone() { + const { compare } = Temporal.PlainDate; + const skippedDay = new Temporal.PlainDate(2011, 12, 30); + const transitionEpoch = 1325239200_000_000_000n; + const beforeOffset = new Temporal.TimeZone("-10:00"); + const afterOffset = new Temporal.TimeZone("+14:00"); + + class CrossDateLineTimeZone extends Temporal.TimeZone { + constructor() { + super("+14:00"); + } + + getOffsetNanosecondsFor(instant) { + if (instant.epochNanoseconds < transitionEpoch) { + return beforeOffset.getOffsetNanosecondsFor(instant); + } + return afterOffset.getOffsetNanosecondsFor(instant); + } + + getPossibleInstantsFor(datetime) { + const comparison = compare(datetime.toPlainDate(), skippedDay); + if (comparison === 0) { + return []; + } + if (comparison < 0) { + return [beforeOffset.getInstantFor(datetime)]; + } + return [afterOffset.getInstantFor(datetime)]; + } + + getPreviousTransition(instant) { + if (instant.epochNanoseconds > transitionEpoch) return new Temporal.Instant(transitionEpoch); + return null; + } + + getNextTransition(instant) { + if (instant.epochNanoseconds < transitionEpoch) return new Temporal.Instant(transitionEpoch); + return null; + } + + toString() { + return "Custom/Date_Line"; + } + } + return new CrossDateLineTimeZone(); + }, + + /* + * observeProperty(calls, object, propertyName, value): + * + * Defines an own property @object.@propertyName with value @value, that + * will log any calls to its accessors to the array @calls. + */ + observeProperty(calls, object, propertyName, value, objectName = "") { + Object.defineProperty(object, propertyName, { + get() { + calls.push(`get ${formatPropertyName(propertyName, objectName)}`); + return value; + }, + set(v) { + calls.push(`set ${formatPropertyName(propertyName, objectName)}`); + } + }); + }, + + /* + * observeMethod(calls, object, propertyName, value): + * + * Defines an own property @object.@propertyName with value @value, that + * will log any calls of @value to the array @calls. + */ + observeMethod(calls, object, propertyName, objectName = "") { + const method = object[propertyName]; + object[propertyName] = function () { + calls.push(`call ${formatPropertyName(propertyName, objectName)}`); + return method.apply(object, arguments); + }; + }, + + /* + * Used for substituteMethod to indicate default behavior instead of a + * substituted value + */ + SUBSTITUTE_SKIP: SKIP_SYMBOL, + + /* + * substituteMethod(object, propertyName, values): + * + * Defines an own property @object.@propertyName that will, for each + * subsequent call to the method previously defined as + * @object.@propertyName: + * - Call the method, if no more values remain + * - Call the method, if the value in @values for the corresponding call + * is SUBSTITUTE_SKIP + * - Otherwise, return the corresponding value in @value + */ + substituteMethod(object, propertyName, values) { + let calls = 0; + const method = object[propertyName]; + object[propertyName] = function () { + if (calls >= values.length) { + return method.apply(object, arguments); + } else if (values[calls] === SKIP_SYMBOL) { + calls++; + return method.apply(object, arguments); + } else { + return values[calls++]; + } + }; + }, + + /* + * calendarObserver: + * A custom calendar that behaves exactly like the ISO 8601 calendar but + * tracks calls to any of its methods, and Get/Has operations on its + * properties, by appending messages to an array. This is for the purpose of + * testing order of operations that are observable from user code. + * objectName is used in the log. + */ + calendarObserver(calls, objectName, methodOverrides = {}) { + function removeExtraHasPropertyChecks(objectName, calls) { + // Inserting the tracking calendar into the return values of methods + // that we chain up into the ISO calendar for, causes extra HasProperty + // checks, which we observe. This removes them so that we don't leak + // implementation details of the helper into the test code. + assert.sameValue(calls.pop(), `has ${objectName}.yearOfWeek`); + assert.sameValue(calls.pop(), `has ${objectName}.yearMonthFromFields`); + assert.sameValue(calls.pop(), `has ${objectName}.year`); + assert.sameValue(calls.pop(), `has ${objectName}.weekOfYear`); + assert.sameValue(calls.pop(), `has ${objectName}.monthsInYear`); + assert.sameValue(calls.pop(), `has ${objectName}.monthDayFromFields`); + assert.sameValue(calls.pop(), `has ${objectName}.monthCode`); + assert.sameValue(calls.pop(), `has ${objectName}.month`); + assert.sameValue(calls.pop(), `has ${objectName}.mergeFields`); + assert.sameValue(calls.pop(), `has ${objectName}.inLeapYear`); + assert.sameValue(calls.pop(), `has ${objectName}.id`); + assert.sameValue(calls.pop(), `has ${objectName}.fields`); + assert.sameValue(calls.pop(), `has ${objectName}.daysInYear`); + assert.sameValue(calls.pop(), `has ${objectName}.daysInWeek`); + assert.sameValue(calls.pop(), `has ${objectName}.daysInMonth`); + assert.sameValue(calls.pop(), `has ${objectName}.dayOfYear`); + assert.sameValue(calls.pop(), `has ${objectName}.dayOfWeek`); + assert.sameValue(calls.pop(), `has ${objectName}.day`); + assert.sameValue(calls.pop(), `has ${objectName}.dateUntil`); + assert.sameValue(calls.pop(), `has ${objectName}.dateFromFields`); + assert.sameValue(calls.pop(), `has ${objectName}.dateAdd`); + } + + const iso8601 = new Temporal.Calendar("iso8601"); + const trackingMethods = { + dateFromFields(...args) { + calls.push(`call ${objectName}.dateFromFields`); + if ('dateFromFields' in methodOverrides) { + const value = methodOverrides.dateFromFields; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.dateFromFields(...args); + // Replace the calendar in the result with the call-tracking calendar + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + yearMonthFromFields(...args) { + calls.push(`call ${objectName}.yearMonthFromFields`); + if ('yearMonthFromFields' in methodOverrides) { + const value = methodOverrides.yearMonthFromFields; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.yearMonthFromFields(...args); + // Replace the calendar in the result with the call-tracking calendar + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + monthDayFromFields(...args) { + calls.push(`call ${objectName}.monthDayFromFields`); + if ('monthDayFromFields' in methodOverrides) { + const value = methodOverrides.monthDayFromFields; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.monthDayFromFields(...args); + // Replace the calendar in the result with the call-tracking calendar + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + dateAdd(...args) { + calls.push(`call ${objectName}.dateAdd`); + if ('dateAdd' in methodOverrides) { + const value = methodOverrides.dateAdd; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.dateAdd(...args); + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + id: "iso8601", + }; + // Automatically generate the other methods that don't need any custom code + [ + "dateUntil", + "day", + "dayOfWeek", + "dayOfYear", + "daysInMonth", + "daysInWeek", + "daysInYear", + "era", + "eraYear", + "fields", + "inLeapYear", + "mergeFields", + "month", + "monthCode", + "monthsInYear", + "toString", + "weekOfYear", + "year", + "yearOfWeek", + ].forEach((methodName) => { + trackingMethods[methodName] = function (...args) { + calls.push(`call ${formatPropertyName(methodName, objectName)}`); + if (methodName in methodOverrides) { + const value = methodOverrides[methodName]; + return typeof value === "function" ? value(...args) : value; + } + return iso8601[methodName](...args); + }; + }); + return new Proxy(trackingMethods, { + get(target, key, receiver) { + const result = Reflect.get(target, key, receiver); + calls.push(`get ${formatPropertyName(key, objectName)}`); + return result; + }, + has(target, key) { + calls.push(`has ${formatPropertyName(key, objectName)}`); + return Reflect.has(target, key); + }, + }); + }, + + /* + * A custom calendar that does not allow any of its methods to be called, for + * the purpose of asserting that a particular operation does not call into + * user code. + */ + calendarThrowEverything() { + class CalendarThrowEverything extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + toString() { + TemporalHelpers.assertUnreachable("toString should not be called"); + } + dateFromFields() { + TemporalHelpers.assertUnreachable("dateFromFields should not be called"); + } + yearMonthFromFields() { + TemporalHelpers.assertUnreachable("yearMonthFromFields should not be called"); + } + monthDayFromFields() { + TemporalHelpers.assertUnreachable("monthDayFromFields should not be called"); + } + dateAdd() { + TemporalHelpers.assertUnreachable("dateAdd should not be called"); + } + dateUntil() { + TemporalHelpers.assertUnreachable("dateUntil should not be called"); + } + era() { + TemporalHelpers.assertUnreachable("era should not be called"); + } + eraYear() { + TemporalHelpers.assertUnreachable("eraYear should not be called"); + } + year() { + TemporalHelpers.assertUnreachable("year should not be called"); + } + month() { + TemporalHelpers.assertUnreachable("month should not be called"); + } + monthCode() { + TemporalHelpers.assertUnreachable("monthCode should not be called"); + } + day() { + TemporalHelpers.assertUnreachable("day should not be called"); + } + fields() { + TemporalHelpers.assertUnreachable("fields should not be called"); + } + mergeFields() { + TemporalHelpers.assertUnreachable("mergeFields should not be called"); + } + } + + return new CalendarThrowEverything(); + }, + + /* + * oneShiftTimeZone(shiftInstant, shiftNanoseconds): + * + * In the case of a spring-forward time zone offset transition (skipped time), + * and disambiguation === 'earlier', BuiltinTimeZoneGetInstantFor subtracts a + * negative number of nanoseconds from a PlainDateTime, which should balance + * with the microseconds field. + * + * This returns an instance of a custom time zone class which skips a length + * of time equal to shiftNanoseconds (a number), at the Temporal.Instant + * shiftInstant. Before shiftInstant, it's identical to UTC, and after + * shiftInstant it's a constant-offset time zone. + * + * It provides a getPossibleInstantsForCalledWith member which is an array + * with the result of calling toString() on any PlainDateTimes passed to + * getPossibleInstantsFor(). + */ + oneShiftTimeZone(shiftInstant, shiftNanoseconds) { + class OneShiftTimeZone extends Temporal.TimeZone { + constructor(shiftInstant, shiftNanoseconds) { + super("+00:00"); + this._shiftInstant = shiftInstant; + this._epoch1 = shiftInstant.epochNanoseconds; + this._epoch2 = this._epoch1 + BigInt(shiftNanoseconds); + this._shiftNanoseconds = shiftNanoseconds; + this._shift = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, this._shiftNanoseconds); + this.getPossibleInstantsForCalledWith = []; + } + + _isBeforeShift(instant) { + return instant.epochNanoseconds < this._epoch1; + } + + getOffsetNanosecondsFor(instant) { + return this._isBeforeShift(instant) ? 0 : this._shiftNanoseconds; + } + + getPossibleInstantsFor(plainDateTime) { + this.getPossibleInstantsForCalledWith.push(plainDateTime.toString({ calendarName: "never" })); + const [instant] = super.getPossibleInstantsFor(plainDateTime); + if (this._shiftNanoseconds > 0) { + if (this._isBeforeShift(instant)) return [instant]; + if (instant.epochNanoseconds < this._epoch2) return []; + return [instant.subtract(this._shift)]; + } + if (instant.epochNanoseconds < this._epoch2) return [instant]; + const shifted = instant.subtract(this._shift); + if (this._isBeforeShift(instant)) return [instant, shifted]; + return [shifted]; + } + + getNextTransition(instant) { + return this._isBeforeShift(instant) ? this._shiftInstant : null; + } + + getPreviousTransition(instant) { + return this._isBeforeShift(instant) ? null : this._shiftInstant; + } + + toString() { + return "Custom/One_Shift"; + } + } + return new OneShiftTimeZone(shiftInstant, shiftNanoseconds); + }, + + /* + * propertyBagObserver(): + * Returns an object that behaves like the given propertyBag but tracks Get + * and Has operations on any of its properties, by appending messages to an + * array. If the value of a property in propertyBag is a primitive, the value + * of the returned object's property will additionally be a + * TemporalHelpers.toPrimitiveObserver that will track calls to its toString + * and valueOf methods in the same array. This is for the purpose of testing + * order of operations that are observable from user code. objectName is used + * in the log. + */ + propertyBagObserver(calls, propertyBag, objectName) { + return new Proxy(propertyBag, { + ownKeys(target) { + calls.push(`ownKeys ${objectName}`); + return Reflect.ownKeys(target); + }, + getOwnPropertyDescriptor(target, key) { + calls.push(`getOwnPropertyDescriptor ${formatPropertyName(key, objectName)}`); + return Reflect.getOwnPropertyDescriptor(target, key); + }, + get(target, key, receiver) { + calls.push(`get ${formatPropertyName(key, objectName)}`); + const result = Reflect.get(target, key, receiver); + if (result === undefined) { + return undefined; + } + if ((result !== null && typeof result === "object") || typeof result === "function") { + return result; + } + return TemporalHelpers.toPrimitiveObserver(calls, result, `${formatPropertyName(key, objectName)}`); + }, + has(target, key) { + calls.push(`has ${formatPropertyName(key, objectName)}`); + return Reflect.has(target, key); + }, + }); + }, + + /* + * specificOffsetTimeZone(): + * + * This returns an instance of a custom time zone class, which returns a + * specific custom value from its getOffsetNanosecondsFrom() method. This is + * for the purpose of testing the validation of what this method returns. + * + * It also returns an empty array from getPossibleInstantsFor(), so as to + * trigger calls to getOffsetNanosecondsFor() when used from the + * BuiltinTimeZoneGetInstantFor operation. + */ + specificOffsetTimeZone(offsetValue) { + class SpecificOffsetTimeZone extends Temporal.TimeZone { + constructor(offsetValue) { + super("UTC"); + this._offsetValue = offsetValue; + } + + getOffsetNanosecondsFor() { + return this._offsetValue; + } + + getPossibleInstantsFor(dt) { + if (typeof this._offsetValue !== 'number' || Math.abs(this._offsetValue) >= 86400e9 || isNaN(this._offsetValue)) return []; + const zdt = dt.toZonedDateTime("UTC").add({ nanoseconds: -this._offsetValue }); + return [zdt.toInstant()]; + } + + get id() { + return this.getOffsetStringFor(new Temporal.Instant(0n)); + } + } + return new SpecificOffsetTimeZone(offsetValue); + }, + + /* + * springForwardFallBackTimeZone(): + * + * This returns an instance of a custom time zone class that implements one + * single spring-forward/fall-back transition, for the purpose of testing the + * disambiguation option, without depending on system time zone data. + * + * The spring-forward occurs at epoch second 954669600 (2000-04-02T02:00 + * local) and goes from offset -08:00 to -07:00. + * + * The fall-back occurs at epoch second 972810000 (2000-10-29T02:00 local) and + * goes from offset -07:00 to -08:00. + */ + springForwardFallBackTimeZone() { + const { compare } = Temporal.PlainDateTime; + const springForwardLocal = new Temporal.PlainDateTime(2000, 4, 2, 2); + const springForwardEpoch = 954669600_000_000_000n; + const fallBackLocal = new Temporal.PlainDateTime(2000, 10, 29, 1); + const fallBackEpoch = 972810000_000_000_000n; + const winterOffset = new Temporal.TimeZone('-08:00'); + const summerOffset = new Temporal.TimeZone('-07:00'); + + class SpringForwardFallBackTimeZone extends Temporal.TimeZone { + constructor() { + super("-08:00"); + } + + getOffsetNanosecondsFor(instant) { + if (instant.epochNanoseconds < springForwardEpoch || + instant.epochNanoseconds >= fallBackEpoch) { + return winterOffset.getOffsetNanosecondsFor(instant); + } + return summerOffset.getOffsetNanosecondsFor(instant); + } + + getPossibleInstantsFor(datetime) { + if (compare(datetime, springForwardLocal) >= 0 && compare(datetime, springForwardLocal.add({ hours: 1 })) < 0) { + return []; + } + if (compare(datetime, fallBackLocal) >= 0 && compare(datetime, fallBackLocal.add({ hours: 1 })) < 0) { + return [summerOffset.getInstantFor(datetime), winterOffset.getInstantFor(datetime)]; + } + if (compare(datetime, springForwardLocal) < 0 || compare(datetime, fallBackLocal) >= 0) { + return [winterOffset.getInstantFor(datetime)]; + } + return [summerOffset.getInstantFor(datetime)]; + } + + getPreviousTransition(instant) { + if (instant.epochNanoseconds > fallBackEpoch) return new Temporal.Instant(fallBackEpoch); + if (instant.epochNanoseconds > springForwardEpoch) return new Temporal.Instant(springForwardEpoch); + return null; + } + + getNextTransition(instant) { + if (instant.epochNanoseconds < springForwardEpoch) return new Temporal.Instant(springForwardEpoch); + if (instant.epochNanoseconds < fallBackEpoch) return new Temporal.Instant(fallBackEpoch); + return null; + } + + get id() { + return "Custom/Spring_Fall"; + } + + toString() { + return "Custom/Spring_Fall"; + } + } + return new SpringForwardFallBackTimeZone(); + }, + + /* + * timeZoneObserver: + * A custom calendar that behaves exactly like the UTC time zone but tracks + * calls to any of its methods, and Get/Has operations on its properties, by + * appending messages to an array. This is for the purpose of testing order of + * operations that are observable from user code. objectName is used in the + * log. methodOverrides is an optional object containing properties with the + * same name as Temporal.TimeZone methods. If the property value is a function + * it will be called with the proper arguments instead of the UTC method. + * Otherwise, the property value will be returned directly. + */ + timeZoneObserver(calls, objectName, methodOverrides = {}) { + const utc = new Temporal.TimeZone("UTC"); + const trackingMethods = { + id: "UTC", + }; + // Automatically generate the methods + ["getOffsetNanosecondsFor", "getPossibleInstantsFor", "toString"].forEach((methodName) => { + trackingMethods[methodName] = function (...args) { + calls.push(`call ${formatPropertyName(methodName, objectName)}`); + if (methodName in methodOverrides) { + const value = methodOverrides[methodName]; + return typeof value === "function" ? value(...args) : value; + } + return utc[methodName](...args); + }; + }); + return new Proxy(trackingMethods, { + get(target, key, receiver) { + const result = Reflect.get(target, key, receiver); + calls.push(`get ${formatPropertyName(key, objectName)}`); + return result; + }, + has(target, key) { + calls.push(`has ${formatPropertyName(key, objectName)}`); + return Reflect.has(target, key); + }, + }); + }, + + /* + * A custom time zone that does not allow any of its methods to be called, for + * the purpose of asserting that a particular operation does not call into + * user code. + */ + timeZoneThrowEverything() { + class TimeZoneThrowEverything extends Temporal.TimeZone { + constructor() { + super("UTC"); + } + getOffsetNanosecondsFor() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be called"); + } + getPossibleInstantsFor() { + TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be called"); + } + toString() { + TemporalHelpers.assertUnreachable("toString should not be called"); + } + } + + return new TimeZoneThrowEverything(); + }, + + /* + * Returns an object that will append logs of any Gets or Calls of its valueOf + * or toString properties to the array calls. Both valueOf and toString will + * return the actual primitiveValue. propertyName is used in the log. + */ + toPrimitiveObserver(calls, primitiveValue, propertyName) { + return { + get valueOf() { + calls.push(`get ${propertyName}.valueOf`); + return function () { + calls.push(`call ${propertyName}.valueOf`); + return primitiveValue; + }; + }, + get toString() { + calls.push(`get ${propertyName}.toString`); + return function () { + calls.push(`call ${propertyName}.toString`); + if (primitiveValue === undefined) return undefined; + return primitiveValue.toString(); + }; + }, + }; + }, + + /* + * An object containing further methods that return arrays of ISO strings, for + * testing parsers. + */ + ISO: { + /* + * PlainMonthDay strings that are not valid. + */ + plainMonthDayStringsInvalid() { + return [ + "11-18junk", + "11-18[u-ca=gregory]", + "11-18[u-ca=hebrew]", + ]; + }, + + /* + * PlainMonthDay strings that are valid and that should produce October 1st. + */ + plainMonthDayStringsValid() { + return [ + "10-01", + "1001", + "1965-10-01", + "1976-10-01T152330.1+00:00", + "19761001T15:23:30.1+00:00", + "1976-10-01T15:23:30.1+0000", + "1976-10-01T152330.1+0000", + "19761001T15:23:30.1+0000", + "19761001T152330.1+00:00", + "19761001T152330.1+0000", + "+001976-10-01T152330.1+00:00", + "+0019761001T15:23:30.1+00:00", + "+001976-10-01T15:23:30.1+0000", + "+001976-10-01T152330.1+0000", + "+0019761001T15:23:30.1+0000", + "+0019761001T152330.1+00:00", + "+0019761001T152330.1+0000", + "1976-10-01T15:23:00", + "1976-10-01T15:23", + "1976-10-01T15", + "1976-10-01", + "--10-01", + "--1001", + ]; + }, + + /* + * PlainTime strings that may be mistaken for PlainMonthDay or + * PlainYearMonth strings, and so require a time designator. + */ + plainTimeStringsAmbiguous() { + const ambiguousStrings = [ + "2021-12", // ambiguity between YYYY-MM and HHMM-UU + "2021-12[-12:00]", // ditto, TZ does not disambiguate + "1214", // ambiguity between MMDD and HHMM + "0229", // ditto, including MMDD that doesn't occur every year + "1130", // ditto, including DD that doesn't occur in every month + "12-14", // ambiguity between MM-DD and HH-UU + "12-14[-14:00]", // ditto, TZ does not disambiguate + "202112", // ambiguity between YYYYMM and HHMMSS + "202112[UTC]", // ditto, TZ does not disambiguate + ]; + // Adding a calendar annotation to one of these strings must not cause + // disambiguation in favour of time. + const stringsWithCalendar = ambiguousStrings.map((s) => s + '[u-ca=iso8601]'); + return ambiguousStrings.concat(stringsWithCalendar); + }, + + /* + * PlainTime strings that are of similar form to PlainMonthDay and + * PlainYearMonth strings, but are not ambiguous due to components that + * aren't valid as months or days. + */ + plainTimeStringsUnambiguous() { + return [ + "2021-13", // 13 is not a month + "202113", // ditto + "2021-13[-13:00]", // ditto + "202113[-13:00]", // ditto + "0000-00", // 0 is not a month + "000000", // ditto + "0000-00[UTC]", // ditto + "000000[UTC]", // ditto + "1314", // 13 is not a month + "13-14", // ditto + "1232", // 32 is not a day + "0230", // 30 is not a day in February + "0631", // 31 is not a day in June + "0000", // 0 is neither a month nor a day + "00-00", // ditto + ]; + }, + + /* + * PlainYearMonth-like strings that are not valid. + */ + plainYearMonthStringsInvalid() { + return [ + "2020-13", + ]; + }, + + /* + * PlainYearMonth-like strings that are valid and should produce November + * 1976 in the ISO 8601 calendar. + */ + plainYearMonthStringsValid() { + return [ + "1976-11", + "1976-11-10", + "1976-11-01T09:00:00+00:00", + "1976-11-01T00:00:00+05:00", + "197611", + "+00197611", + "1976-11-18T15:23:30.1\u221202:00", + "1976-11-18T152330.1+00:00", + "19761118T15:23:30.1+00:00", + "1976-11-18T15:23:30.1+0000", + "1976-11-18T152330.1+0000", + "19761118T15:23:30.1+0000", + "19761118T152330.1+00:00", + "19761118T152330.1+0000", + "+001976-11-18T152330.1+00:00", + "+0019761118T15:23:30.1+00:00", + "+001976-11-18T15:23:30.1+0000", + "+001976-11-18T152330.1+0000", + "+0019761118T15:23:30.1+0000", + "+0019761118T152330.1+00:00", + "+0019761118T152330.1+0000", + "1976-11-18T15:23", + "1976-11-18T15", + "1976-11-18", + ]; + }, + + /* + * PlainYearMonth-like strings that are valid and should produce November of + * the ISO year -9999. + */ + plainYearMonthStringsValidNegativeYear() { + return [ + "\u2212009999-11", + ]; + }, + } +}; diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/since.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/since.js new file mode 100644 index 0000000000..ff64fa3160 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/since.js @@ -0,0 +1,310 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.since() +features: [Temporal] +---*/ + +var zdt = Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456789+01:00[+01:00]"); + +// zdt.since(earlier) === earlier.until(zdt) with default options +var earlier = Temporal.ZonedDateTime.from({ + year: 1966, + month: 3, + day: 3, + hour: 18, + timeZone: "+01:00" +}); +assert.sameValue(`${ zdt.since(earlier) }`, `${ earlier.until(zdt) }`); + +// casts argument +assert.sameValue(`${ zdt.since({ + year: 2019, + month: 10, + day: 29, + hour: 10, + timeZone: "+01:00" +}) }`, "-PT376434H36M29.876543211S"); +assert.sameValue(`${ zdt.since("2019-10-29T10:46:38.271986102+01:00[+01:00]") }`, "-PT376435H23M8.148529313S"); +var feb20 = Temporal.ZonedDateTime.from("2020-02-01T00:00+01:00[+01:00]"); +var feb21 = Temporal.ZonedDateTime.from("2021-02-01T00:00+01:00[+01:00]"); + +// defaults to returning hours +assert.sameValue(`${ feb21.since(feb20) }`, "PT8784H"); +assert.sameValue(`${ feb21.since(feb20, { largestUnit: "auto" }) }`, "PT8784H"); +assert.sameValue(`${ feb21.since(feb20, { largestUnit: "hours" }) }`, "PT8784H"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("2021-02-01T00:00:00.000000001+01:00[+01:00]").since(feb20) }`, "PT8784H0.000000001S"); +assert.sameValue(`${ feb21.since(Temporal.ZonedDateTime.from("2020-02-01T00:00:00.000000001+01:00[+01:00]")) }`, "PT8783H59M59.999999999S"); + +// can return lower or higher units +assert.sameValue(`${ feb21.since(feb20, { largestUnit: "years" }) }`, "P1Y"); +assert.sameValue(`${ feb21.since(feb20, { largestUnit: "months" }) }`, "P12M"); +assert.sameValue(`${ feb21.since(feb20, { largestUnit: "weeks" }) }`, "P52W2D"); +assert.sameValue(`${ feb21.since(feb20, { largestUnit: "days" }) }`, "P366D"); +assert.sameValue(`${ feb21.since(feb20, { largestUnit: "minutes" }) }`, "PT527040M"); +assert.sameValue(`${ feb21.since(feb20, { largestUnit: "seconds" }) }`, "PT31622400S"); + +// can return subseconds +var later = feb20.add({ + days: 1, + milliseconds: 250, + microseconds: 250, + nanoseconds: 250 +}); +var msDiff = later.since(feb20, { largestUnit: "milliseconds" }); +assert.sameValue(msDiff.seconds, 0); +assert.sameValue(msDiff.milliseconds, 86400250); +assert.sameValue(msDiff.microseconds, 250); +assert.sameValue(msDiff.nanoseconds, 250); +var µsDiff = later.since(feb20, { largestUnit: "microseconds" }); +assert.sameValue(µsDiff.milliseconds, 0); +assert.sameValue(µsDiff.microseconds, 86400250250); +assert.sameValue(µsDiff.nanoseconds, 250); +var nsDiff = later.since(feb20, { largestUnit: "nanoseconds" }); +assert.sameValue(nsDiff.microseconds, 0); +assert.sameValue(nsDiff.nanoseconds, 86400250250250); + +// does not include higher units than necessary +var lastFeb20 = Temporal.ZonedDateTime.from("2020-02-29T00:00+01:00[+01:00]"); +var lastFeb21 = Temporal.ZonedDateTime.from("2021-02-28T00:00+01:00[+01:00]"); +assert.sameValue(`${ lastFeb21.since(lastFeb20) }`, "PT8760H"); +assert.sameValue(`${ lastFeb21.since(lastFeb20, { largestUnit: "months" }) }`, "P11M28D"); +assert.sameValue(`${ lastFeb21.since(lastFeb20, { largestUnit: "years" }) }`, "P11M28D"); + +// weeks and months are mutually exclusive +var laterDateTime = zdt.add({ + days: 42, + hours: 3 +}); +var weeksDifference = laterDateTime.since(zdt, { largestUnit: "weeks" }); +assert.notSameValue(weeksDifference.weeks, 0); +assert.sameValue(weeksDifference.months, 0); +var monthsDifference = laterDateTime.since(zdt, { largestUnit: "months" }); +assert.sameValue(monthsDifference.weeks, 0); +assert.notSameValue(monthsDifference.months, 0); + +// no two different calendars +var zdt1 = new Temporal.ZonedDateTime(0n, "UTC"); +var fakeJapanese = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "japanese", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +var zdt2 = new Temporal.ZonedDateTime(0n, "UTC", fakeJapanese); +assert.throws(RangeError, () => zdt1.since(zdt2)); + +var earlier = Temporal.ZonedDateTime.from('2019-01-08T09:22:36.123456789+01:00[+01:00]'); +var later = Temporal.ZonedDateTime.from('2021-09-07T13:39:40.987654321+01:00[+01:00]'); +// assumes a different default for largestUnit if smallestUnit is larger than days +assert.sameValue(`${ later.since(earlier, { + smallestUnit: "years", + roundingMode: "halfExpand" +}) }`, "P3Y"); +assert.sameValue(`${ later.since(earlier, { + smallestUnit: "months", + roundingMode: "halfExpand" +}) }`, "P32M"); +assert.sameValue(`${ later.since(earlier, { + smallestUnit: "weeks", + roundingMode: "halfExpand" +}) }`, "P139W"); + +// rounds to an increment of hours +assert.sameValue(`${ later.since(earlier, { + smallestUnit: "hours", + roundingIncrement: 3, + roundingMode: "halfExpand" +}) }`, "PT23355H"); + +// rounds to an increment of minutes +assert.sameValue(`${ later.since(earlier, { + smallestUnit: "minutes", + roundingIncrement: 30, + roundingMode: "halfExpand" +}) }`, "PT23356H30M"); + +// rounds to an increment of seconds +assert.sameValue(`${ later.since(earlier, { + smallestUnit: "seconds", + roundingIncrement: 15, + roundingMode: "halfExpand" +}) }`, "PT23356H17M"); + +// rounds to an increment of milliseconds +assert.sameValue(`${ later.since(earlier, { + smallestUnit: "milliseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT23356H17M4.86S"); + +// rounds to an increment of microseconds +assert.sameValue(`${ later.since(earlier, { + smallestUnit: "microseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT23356H17M4.8642S"); + +// rounds to an increment of nanoseconds +assert.sameValue(`${ later.since(earlier, { + smallestUnit: "nanoseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT23356H17M4.86419753S"); + +// valid hour increments divide into 24 +[ + 1, + 2, + 3, + 4, + 6, + 8, + 12 +].forEach(roundingIncrement => { + var options = { + smallestUnit: "hours", + roundingIncrement + }; + assert(later.since(earlier, options) instanceof Temporal.Duration); +}); +[ + "minutes", + "seconds" +].forEach(smallestUnit => { + [ + 1, + 2, + 3, + 4, + 5, + 6, + 10, + 12, + 15, + 20, + 30 + ].forEach(roundingIncrement => { + var options = { + smallestUnit, + roundingIncrement + }; + assert(later.since(earlier, options) instanceof Temporal.Duration); + }); +}); +[ + "milliseconds", + "microseconds", + "nanoseconds" +].forEach(smallestUnit => { + [ + 1, + 2, + 4, + 5, + 8, + 10, + 20, + 25, + 40, + 50, + 100, + 125, + 200, + 250, + 500 + ].forEach(roundingIncrement => { + var options = { + smallestUnit, + roundingIncrement + }; + assert(later.since(earlier, options) instanceof Temporal.Duration); + }); +}); + +// throws on increments that do not divide evenly into the next highest +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "hours", + roundingIncrement: 11 +})); +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "minutes", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "seconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "milliseconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "microseconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "nanoseconds", + roundingIncrement: 29 +})); + +// throws on increments that are equal to the next highest +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "hours", + roundingIncrement: 24 +})); +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "minutes", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "seconds", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "milliseconds", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "microseconds", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "nanoseconds", + roundingIncrement: 1000 +})); + +// rounds relative to the receiver +var dt1 = Temporal.ZonedDateTime.from("2019-01-01T00:00+00:00[UTC]"); +var dt2 = Temporal.ZonedDateTime.from("2020-07-02T00:00+00:00[UTC]"); +assert.sameValue(`${ dt2.since(dt1, { + smallestUnit: "years", + roundingMode: "halfExpand" +}) }`, "P1Y"); +assert.sameValue(`${ dt1.since(dt2, { + smallestUnit: "years", + roundingMode: "halfExpand" +}) }`, "-P2Y"); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/string-parsing.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/string-parsing.js new file mode 100644 index 0000000000..f17eca11fd --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/string-parsing.js @@ -0,0 +1,71 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: string parsing +features: [Temporal] +---*/ + +// any number of decimal places +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.1-08:00[-08:00]") }`, "1976-11-18T15:23:30.1-08:00[-08:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.12-08:00[-08:00]") }`, "1976-11-18T15:23:30.12-08:00[-08:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123-08:00[-08:00]") }`, "1976-11-18T15:23:30.123-08:00[-08:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.1234-08:00[-08:00]") }`, "1976-11-18T15:23:30.1234-08:00[-08:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.12345-08:00[-08:00]") }`, "1976-11-18T15:23:30.12345-08:00[-08:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456-08:00[-08:00]") }`, "1976-11-18T15:23:30.123456-08:00[-08:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.1234567-08:00[-08:00]") }`, "1976-11-18T15:23:30.1234567-08:00[-08:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.12345678-08:00[-08:00]") }`, "1976-11-18T15:23:30.12345678-08:00[-08:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456789-08:00[-08:00]") }`, "1976-11-18T15:23:30.123456789-08:00[-08:00]"); + +// variant decimal separator +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30,12-08:00[-08:00]") }`, "1976-11-18T15:23:30.12-08:00[-08:00]"); + +// variant minus sign +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.12\u221208:00[-08:00]") }`, "1976-11-18T15:23:30.12-08:00[-08:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("\u2212009999-11-18T15:23:30.12+00:00[UTC]") }`, "-009999-11-18T15:23:30.12+00:00[UTC]"); + +// mixture of basic and extended format +[ + "1976-11-18T152330.1-08:00[-08:00]", + "19761118T15:23:30.1-08:00[-08:00]", + "1976-11-18T15:23:30.1-0800[-08:00]", + "1976-11-18T152330.1-0800[-08:00]", + "19761118T15:23:30.1-0800[-08:00]", + "19761118T152330.1-08:00[-08:00]", + "19761118T152330.1-0800[-08:00]", + "+001976-11-18T152330.1-08:00[-08:00]", + "+0019761118T15:23:30.1-08:00[-08:00]", + "+001976-11-18T15:23:30.1-0800[-08:00]", + "+001976-11-18T152330.1-0800[-08:00]", + "+0019761118T15:23:30.1-0800[-08:00]", + "+0019761118T152330.1-08:00[-08:00]", + "+0019761118T152330.1-0800[-08:00]" +].forEach(input => assert.sameValue(`${ Temporal.ZonedDateTime.from(input) }`, "1976-11-18T15:23:30.1-08:00[-08:00]")); + +// optional parts +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30-08[-08:00]") }`, "1976-11-18T15:23:30-08:00[-08:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15-08:00[-08:00]") }`, "1976-11-18T15:00:00-08:00[-08:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("2020-01-01[+09:00]") }`, "2020-01-01T00:00:00+09:00[+09:00]"); + +// no junk at end of string +assert.throws(RangeError, () => Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456789-08:00[-08:00]junk")) + +// constrain has no effect on invalid ISO string +assert.throws(RangeError, () => Temporal.ZonedDateTime.from("2020-13-34T24:60[-08:00]", { overflow: "constrain" })); + +// { offset: 'reject' } throws if offset does not match IANA time zone +assert.throws(RangeError, () => Temporal.ZonedDateTime.from("2020-03-08T01:00-04:00[UTC]")); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from("2020-03-08T01:00-04:00[UTC]", { offset: "reject" })); + +// throw when bad disambiguation +[ + "", + "EARLIER", + "balance" +].forEach(disambiguation => { + assert.throws(RangeError, () => Temporal.ZonedDateTime.from("2020-11-01T04:00[-08:00]", { disambiguation })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/subtract.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/subtract.js new file mode 100644 index 0000000000..790dfbb9cb --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/subtract.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.subtract() +features: [Temporal] +---*/ + +var zdt = Temporal.ZonedDateTime.from("1969-12-25T12:23:45.678901234+00:00[UTC]"); + +// inst.subtract(durationObj) +var earlier = zdt.subtract(Temporal.Duration.from("PT240H0.000000800S")); +assert.sameValue(`${ earlier }`, "1969-12-15T12:23:45.678900434+00:00[UTC]"); + +// casts argument +assert.sameValue(`${ zdt.subtract("PT240H0.000000800S") }`, "1969-12-15T12:23:45.678900434+00:00[UTC]"); +var mar31 = Temporal.ZonedDateTime.from("2020-03-31T15:00+00:00[UTC]"); + +// constrain when ambiguous result +assert.sameValue(`${ mar31.subtract({ months: 1 }) }`, "2020-02-29T15:00:00+00:00[UTC]"); +assert.sameValue(`${ mar31.subtract({ months: 1 }, { overflow: "constrain" }) }`, "2020-02-29T15:00:00+00:00[UTC]"); + +// symmetrical with regard to negative durations in the time part +assert.sameValue(`${ mar31.subtract({ minutes: -30 }) }`, "2020-03-31T15:30:00+00:00[UTC]"); +assert.sameValue(`${ mar31.subtract({ seconds: -30 }) }`, "2020-03-31T15:00:30+00:00[UTC]"); + +// throw when ambiguous result with reject +assert.throws(RangeError, () => mar31.subtract({ months: 1 }, { overflow: "reject" })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toInstant.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toInstant.js new file mode 100644 index 0000000000..0fcc34bb13 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toInstant.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.toInstant() +features: [Temporal] +---*/ + + +// recent date +var zdt = Temporal.ZonedDateTime.from("2019-10-29T10:46:38.271986102+01:00[+01:00]"); +assert.sameValue(`${ zdt.toInstant() }`, "2019-10-29T09:46:38.271986102Z"); + +// year ≤ 99 +var zdt = Temporal.ZonedDateTime.from("0098-10-29T10:46:38.271986102+00:00[UTC]"); +assert.sameValue(`${ zdt.toInstant() }`, "0098-10-29T10:46:38.271986102Z"); +zdt = Temporal.ZonedDateTime.from("+000098-10-29T10:46:38.271986102+00:00[UTC]"); +assert.sameValue(`${ zdt.toInstant() }`, "0098-10-29T10:46:38.271986102Z"); + +// year < 1 +var zdt = Temporal.ZonedDateTime.from("0000-10-29T10:46:38.271986102+00:00[UTC]"); +assert.sameValue(`${ zdt.toInstant() }`, "0000-10-29T10:46:38.271986102Z"); +zdt = Temporal.ZonedDateTime.from("+000000-10-29T10:46:38.271986102+00:00[UTC]"); +assert.sameValue(`${ zdt.toInstant() }`, "0000-10-29T10:46:38.271986102Z"); +zdt = Temporal.ZonedDateTime.from("-001000-10-29T10:46:38.271986102+00:00[UTC]"); +assert.sameValue(`${ zdt.toInstant() }`, "-001000-10-29T10:46:38.271986102Z"); + +// year 0 leap day +var zdt = Temporal.ZonedDateTime.from("0000-02-29T00:00-00:01[-00:01]"); +assert.sameValue(`${ zdt.toInstant() }`, "0000-02-29T00:01:00Z"); +zdt = Temporal.ZonedDateTime.from("+000000-02-29T00:00-00:01[-00:01]"); +assert.sameValue(`${ zdt.toInstant() }`, "0000-02-29T00:01:00Z"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainDate.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainDate.js new file mode 100644 index 0000000000..4b8e6aa54e --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainDate.js @@ -0,0 +1,47 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.toPlainDate() +features: [Temporal] +---*/ + +var tz = new Temporal.TimeZone("-07:00"); + +// works +var zdt = Temporal.Instant.from("2019-10-29T09:46:38.271986102Z").toZonedDateTimeISO(tz); +assert.sameValue(`${ zdt.toPlainDate() }`, "2019-10-29"); + +// preserves the calendar +const fakeGregorian = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "gregory", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +var zdt = Temporal.Instant.from("2019-10-29T09:46:38.271986102Z").toZonedDateTime({ + timeZone: tz, + calendar: fakeGregorian +}); +assert.sameValue(zdt.toPlainDate().getCalendar(), fakeGregorian); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainMonthDay.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainMonthDay.js new file mode 100644 index 0000000000..23d1248aec --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainMonthDay.js @@ -0,0 +1,51 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.toPlainMonthDay() +features: [Temporal] +---*/ + +var tz = new Temporal.TimeZone("-08:00"); + +// works +var zdt = Temporal.Instant.from("2019-10-29T09:46:38.271986102Z").toZonedDateTimeISO(tz); +assert.sameValue(`${ zdt.toPlainMonthDay() }`, "10-29"); + +// preserves the calendar +var fakeGregorian = { + id: 'gregory', + monthDayFromFields(fields) { + var md = Temporal.Calendar.from("iso8601").monthDayFromFields(fields); + var {isoYear, isoMonth, isoDay} = md.getISOFields(); + return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); + }, + monthCode(date) { return date.withCalendar("iso8601").monthCode; }, + day(date) { return date.withCalendar("iso8601").day; }, + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields(fieldNames) { return fieldNames; }, + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +var zdt = Temporal.Instant.from("2019-10-29T09:46:38.271986102Z").toZonedDateTime({ + timeZone: tz, + calendar: fakeGregorian +}); +assert.sameValue(zdt.toPlainMonthDay().getCalendar(), fakeGregorian); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainTime.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainTime.js new file mode 100644 index 0000000000..78e6e0bbae --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainTime.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.toPlainTime() +features: [Temporal] +---*/ + +var tz = new Temporal.TimeZone("-07:00"); + +// works +var zdt = Temporal.Instant.from("2019-10-29T09:46:38.271986102Z").toZonedDateTimeISO(tz); +assert.sameValue(`${ zdt.toPlainTime() }`, "02:46:38.271986102"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainYearMonth.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainYearMonth.js new file mode 100644 index 0000000000..983bc21bec --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainYearMonth.js @@ -0,0 +1,51 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.toPlainYearMonth() +features: [Temporal] +---*/ + +var tz = new Temporal.TimeZone("-08:00"); + +// works +var zdt = Temporal.Instant.from("2019-10-29T09:46:38.271986102Z").toZonedDateTimeISO(tz); +assert.sameValue(`${ zdt.toPlainYearMonth() }`, "2019-10"); + +// preserves the calendar +var fakeGregorian = { + id: 'gregory', + yearMonthFromFields(fields) { + var ym = Temporal.Calendar.from("iso8601").yearMonthFromFields(fields); + var {isoYear, isoMonth, isoDay} = ym.getISOFields(); + return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); + }, + year(date) { return date.withCalendar("iso8601").year; }, + monthCode(date) { return date.withCalendar("iso8601").monthCode; }, + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields(fieldNames) { return fieldNames; }, + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + yearOfWeek() {}, +}; +var zdt = Temporal.Instant.from("2019-10-29T09:46:38.271986102Z").toZonedDateTime({ + timeZone: tz, + calendar: fakeGregorian +}); +assert.sameValue(zdt.toPlainYearMonth().getCalendar(), fakeGregorian); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toString.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toString.js new file mode 100644 index 0000000000..2e3751b520 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toString.js @@ -0,0 +1,74 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.toString() +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var zdt1 = Temporal.ZonedDateTime.from("1976-11-18T15:23+00:00[UTC]"); +var fakeGregorian = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "gregory", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; + +// shows offset if offset = auto +assert.sameValue(zdt1.toString({ offset: "auto" }), "1976-11-18T15:23:00+00:00[UTC]"); + +// omits offset if offset = never +assert.sameValue(zdt1.toString({ offset: "never" }), "1976-11-18T15:23:00[UTC]"); + +// combinations of calendar, time zone, and offset +var zdt = zdt1.withCalendar(fakeGregorian); +assert.sameValue(zdt.toString({ + timeZoneName: "never", + calendarName: "never" +}), "1976-11-18T15:23:00+00:00"); +assert.sameValue(zdt.toString({ + offset: "never", + calendarName: "never" +}), "1976-11-18T15:23:00[UTC]"); +assert.sameValue(zdt.toString({ + offset: "never", + timeZoneName: "never" +}), "1976-11-18T15:23:00[u-ca=gregory]"); +assert.sameValue(zdt.toString({ + offset: "never", + timeZoneName: "never", + calendarName: "never" +}), "1976-11-18T15:23:00"); + +// rounding up to a nonexistent wall-clock time +var dst = TemporalHelpers.springForwardFallBackTimeZone(); +var zdt5 = Temporal.PlainDateTime.from("2000-04-02T01:59:59.999999999").toZonedDateTime(dst); +var roundedString = zdt5.toString({ + fractionalSecondDigits: 8, + roundingMode: "halfExpand" +}); +assert.sameValue(roundedString, "2000-04-02T03:00:00.00000000-07:00[Custom/Spring_Fall]"); +var zdt6 = Temporal.Instant.from(roundedString); +assert.sameValue(zdt6.epochNanoseconds - zdt5.epochNanoseconds, 1n); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/until.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/until.js new file mode 100644 index 0000000000..65fdcf2ffc --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/until.js @@ -0,0 +1,314 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.until() +features: [Temporal] +---*/ + +var zdt = Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456789+01:00[+01:00]"); + +// zdt.until(later) === later.since(zdt) with default options +var later = Temporal.ZonedDateTime.from({ + year: 2016, + month: 3, + day: 3, + hour: 18, + timeZone: "+01:00" +}); +assert.sameValue(`${ zdt.until(later) }`, `${ later.since(zdt) }`); + +// casts argument +assert.sameValue(`${ zdt.until({ + year: 2019, + month: 10, + day: 29, + hour: 10, + timeZone: "+01:00" +}) }`, "PT376434H36M29.876543211S"); +assert.sameValue(`${ zdt.until("2019-10-29T10:46:38.271986102+01:00[+01:00]") }`, "PT376435H23M8.148529313S"); +var feb20 = Temporal.ZonedDateTime.from("2020-02-01T00:00+01:00[+01:00]"); +var feb21 = Temporal.ZonedDateTime.from("2021-02-01T00:00+01:00[+01:00]"); + +// defaults to returning hours +assert.sameValue(`${ feb20.until(feb21) }`, "PT8784H"); +assert.sameValue(`${ feb20.until(feb21, { largestUnit: "auto" }) }`, "PT8784H"); +assert.sameValue(`${ feb20.until(feb21, { largestUnit: "hours" }) }`, "PT8784H"); +assert.sameValue(`${ feb20.until(Temporal.ZonedDateTime.from("2021-02-01T00:00:00.000000001+01:00[+01:00]")) }`, "PT8784H0.000000001S"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("2020-02-01T00:00:00.000000001+01:00[+01:00]").until(feb21) }`, "PT8783H59M59.999999999S"); + +// can return lower or higher units +assert.sameValue(`${ feb20.until(feb21, { largestUnit: "years" }) }`, "P1Y"); +assert.sameValue(`${ feb20.until(feb21, { largestUnit: "months" }) }`, "P12M"); +assert.sameValue(`${ feb20.until(feb21, { largestUnit: "weeks" }) }`, "P52W2D"); +assert.sameValue(`${ feb20.until(feb21, { largestUnit: "days" }) }`, "P366D"); +assert.sameValue(`${ feb20.until(feb21, { largestUnit: "minutes" }) }`, "PT527040M"); +assert.sameValue(`${ feb20.until(feb21, { largestUnit: "seconds" }) }`, "PT31622400S"); + +// can return subseconds +var later = feb20.add({ + days: 1, + milliseconds: 250, + microseconds: 250, + nanoseconds: 250 +}); +var msDiff = feb20.until(later, { largestUnit: "milliseconds" }); +assert.sameValue(msDiff.seconds, 0); +assert.sameValue(msDiff.milliseconds, 86400250); +assert.sameValue(msDiff.microseconds, 250); +assert.sameValue(msDiff.nanoseconds, 250); +var µsDiff = feb20.until(later, { largestUnit: "microseconds" }); +assert.sameValue(µsDiff.milliseconds, 0); +assert.sameValue(µsDiff.microseconds, 86400250250); +assert.sameValue(µsDiff.nanoseconds, 250); +var nsDiff = feb20.until(later, { largestUnit: "nanoseconds" }); +assert.sameValue(nsDiff.microseconds, 0); +assert.sameValue(nsDiff.nanoseconds, 86400250250250); + +// does not include higher units than necessary +var lastFeb20 = Temporal.ZonedDateTime.from("2020-02-29T00:00+01:00[+01:00]"); +var lastJan21 = Temporal.ZonedDateTime.from("2021-01-31T00:00+01:00[+01:00]"); +assert.sameValue(`${ lastFeb20.until(lastJan21) }`, "PT8088H"); +assert.sameValue(`${ lastFeb20.until(lastJan21, { largestUnit: "months" }) }`, "P11M2D"); +assert.sameValue(`${ lastFeb20.until(lastJan21, { largestUnit: "years" }) }`, "P11M2D"); + +// weeks and months are mutually exclusive +var laterDateTime = zdt.add({ + days: 42, + hours: 3 +}); +var weeksDifference = zdt.until(laterDateTime, { largestUnit: "weeks" }); +assert.notSameValue(weeksDifference.weeks, 0); +assert.sameValue(weeksDifference.months, 0); +var monthsDifference = zdt.until(laterDateTime, { largestUnit: "months" }); +assert.sameValue(monthsDifference.weeks, 0); +assert.notSameValue(monthsDifference.months, 0); + +// no two different calendars +var zdt1 = new Temporal.ZonedDateTime(0n, "UTC"); +var fakeJapanese = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "japanese", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +var zdt2 = new Temporal.ZonedDateTime(0n, "UTC", fakeJapanese); +assert.throws(RangeError, () => zdt1.until(zdt2)); + +var earlier = Temporal.ZonedDateTime.from('2019-01-08T09:22:36.123456789+01:00[+01:00]'); +var later = Temporal.ZonedDateTime.from('2021-09-07T13:39:40.987654321+01:00[+01:00]'); +// assumes a different default for largestUnit if smallestUnit is larger than hours +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "years", + roundingMode: "halfExpand" +}) }`, "P3Y"); +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "months", + roundingMode: "halfExpand" +}) }`, "P32M"); +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "weeks", + roundingMode: "halfExpand" +}) }`, "P139W"); +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "days", + roundingMode: "halfExpand" +}) }`, "P973D"); + +// rounds to an increment of hours +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "hours", + roundingIncrement: 3, + roundingMode: "halfExpand" +}) }`, "PT23355H"); + +// rounds to an increment of minutes +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "minutes", + roundingIncrement: 30, + roundingMode: "halfExpand" +}) }`, "PT23356H30M"); + +// rounds to an increment of seconds +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "seconds", + roundingIncrement: 15, + roundingMode: "halfExpand" +}) }`, "PT23356H17M"); + +// rounds to an increment of milliseconds +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "milliseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT23356H17M4.86S"); + +// rounds to an increment of microseconds +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "microseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT23356H17M4.8642S"); + +// rounds to an increment of nanoseconds +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "nanoseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT23356H17M4.86419753S"); + +// valid hour increments divide into 24 +[ + 1, + 2, + 3, + 4, + 6, + 8, + 12 +].forEach(roundingIncrement => { + var options = { + smallestUnit: "hours", + roundingIncrement + }; + assert(earlier.until(later, options) instanceof Temporal.Duration); +}); +[ + "minutes", + "seconds" +].forEach(smallestUnit => { + [ + 1, + 2, + 3, + 4, + 5, + 6, + 10, + 12, + 15, + 20, + 30 + ].forEach(roundingIncrement => { + var options = { + smallestUnit, + roundingIncrement + }; + assert(earlier.until(later, options) instanceof Temporal.Duration); + }); +}); +[ + "milliseconds", + "microseconds", + "nanoseconds" +].forEach(smallestUnit => { + [ + 1, + 2, + 4, + 5, + 8, + 10, + 20, + 25, + 40, + 50, + 100, + 125, + 200, + 250, + 500 + ].forEach(roundingIncrement => { + var options = { + smallestUnit, + roundingIncrement + }; + assert(earlier.until(later, options) instanceof Temporal.Duration); + }); +}); + +// throws on increments that do not divide evenly into the next highest +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "hours", + roundingIncrement: 11 +})); +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "minutes", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "seconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "milliseconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "microseconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "nanoseconds", + roundingIncrement: 29 +})); + +// throws on increments that are equal to the next highest +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "hours", + roundingIncrement: 24 +})); +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "minutes", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "seconds", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "milliseconds", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "microseconds", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "nanoseconds", + roundingIncrement: 1000 +})); + +// rounds relative to the receiver +var dt1 = Temporal.ZonedDateTime.from("2019-01-01T00:00+00:00[UTC]"); +var dt2 = Temporal.ZonedDateTime.from("2020-07-02T00:00+00:00[UTC]"); +assert.sameValue(`${ dt1.until(dt2, { + smallestUnit: "years", + roundingMode: "halfExpand" +}) }`, "P2Y"); +assert.sameValue(`${ dt2.until(dt1, { + smallestUnit: "years", + roundingMode: "halfExpand" +}) }`, "-P1Y"); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/with.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/with.js new file mode 100644 index 0000000000..42eede22f9 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/with.js @@ -0,0 +1,272 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.with() +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var zdt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789).toZonedDateTime("UTC"); + +// zdt.with({ year: 2019 } works +assert.sameValue(`${ zdt.with({ year: 2019 }) }`, "2019-11-18T15:23:30.123456789+00:00[UTC]"); + +// zdt.with({ month: 5 } works +assert.sameValue(`${ zdt.with({ month: 5 }) }`, "1976-05-18T15:23:30.123456789+00:00[UTC]"); + +// zdt.with({ monthCode: "M05" }) works +assert.sameValue(`${ zdt.with({ monthCode: "M05" }) }`, "1976-05-18T15:23:30.123456789+00:00[UTC]"); + +// month and monthCode must agree +assert.throws(RangeError, () => zdt.with({ + month: 5, + monthCode: "M06" +})); + +// zdt.with({ day: 5 } works +assert.sameValue(`${ zdt.with({ day: 5 }) }`, "1976-11-05T15:23:30.123456789+00:00[UTC]"); + +// zdt.with({ hour: 5 } works +assert.sameValue(`${ zdt.with({ hour: 5 }) }`, "1976-11-18T05:23:30.123456789+00:00[UTC]"); + +// zdt.with({ minute: 5 } works +assert.sameValue(`${ zdt.with({ minute: 5 }) }`, "1976-11-18T15:05:30.123456789+00:00[UTC]"); + +// zdt.with({ second: 5 } works +assert.sameValue(`${ zdt.with({ second: 5 }) }`, "1976-11-18T15:23:05.123456789+00:00[UTC]"); + +// zdt.with({ millisecond: 5 } works +assert.sameValue(`${ zdt.with({ millisecond: 5 }) }`, "1976-11-18T15:23:30.005456789+00:00[UTC]"); + +// zdt.with({ microsecond: 5 } works +assert.sameValue(`${ zdt.with({ microsecond: 5 }) }`, "1976-11-18T15:23:30.123005789+00:00[UTC]"); + +// zdt.with({ nanosecond: 5 } works +assert.sameValue(`${ zdt.with({ nanosecond: 5 }) }`, "1976-11-18T15:23:30.123456005+00:00[UTC]"); + +// zdt.with({ month: 5, second: 15 } works +assert.sameValue(`${ zdt.with({ + month: 5, + second: 15 +}) }`, "1976-05-18T15:23:15.123456789+00:00[UTC]"); + +// Overflow options +// constrain +var overflow = "constrain"; +assert.sameValue(`${ zdt.with({ month: 29 }, { overflow }) }`, "1976-12-18T15:23:30.123456789+00:00[UTC]"); +assert.sameValue(`${ zdt.with({ day: 31 }, { overflow }) }`, "1976-11-30T15:23:30.123456789+00:00[UTC]"); +assert.sameValue(`${ zdt.with({ hour: 29 }, { overflow }) }`, "1976-11-18T23:23:30.123456789+00:00[UTC]"); +assert.sameValue(`${ zdt.with({ nanosecond: 9000 }, { overflow }) }`, "1976-11-18T15:23:30.123456999+00:00[UTC]"); + +// reject +var overflow = "reject"; +assert.throws(RangeError, () => zdt.with({ month: 29 }, { overflow })); +assert.throws(RangeError, () => zdt.with({ day: 31 }, { overflow })); +assert.throws(RangeError, () => zdt.with({ hour: 29 }, { overflow })); +assert.throws(RangeError, () => zdt.with({ nanosecond: 9000 }, { overflow })); + +var dst = TemporalHelpers.springForwardFallBackTimeZone(); +var dstStartDay = Temporal.PlainDateTime.from("2000-04-02T12:00:01").toZonedDateTime(dst); +var dstEndDay = Temporal.PlainDateTime.from("2000-10-29T12:00:01").toZonedDateTime(dst); +var oneThirty = { +hour: 1, +minute: 30 +}; +var twoThirty = { +hour: 2, +minute: 30 +}; + +// Disambiguation options +var offset = "ignore"; +// compatible, skipped wall time +assert.sameValue(`${ dstStartDay.with(twoThirty, { + offset, + disambiguation: "compatible" +}) }`, "2000-04-02T03:30:01-07:00[Custom/Spring_Fall]"); + +// earlier, skipped wall time +assert.sameValue(`${ dstStartDay.with(twoThirty, { + offset, + disambiguation: "earlier" +}) }`, "2000-04-02T01:30:01-08:00[Custom/Spring_Fall]"); + +// later, skipped wall time +assert.sameValue(`${ dstStartDay.with(twoThirty, { + offset, + disambiguation: "later" +}) }`, "2000-04-02T03:30:01-07:00[Custom/Spring_Fall]"); + +// compatible, repeated wall time +assert.sameValue(`${ dstEndDay.with(oneThirty, { + offset, + disambiguation: "compatible" +}) }`, "2000-10-29T01:30:01-07:00[Custom/Spring_Fall]"); + +// earlier, repeated wall time +assert.sameValue(`${ dstEndDay.with(oneThirty, { + offset, + disambiguation: "earlier" +}) }`, "2000-10-29T01:30:01-07:00[Custom/Spring_Fall]"); + +// later, repeated wall time +assert.sameValue(`${ dstEndDay.with(oneThirty, { + offset, + disambiguation: "later" +}) }`, "2000-10-29T01:30:01-08:00[Custom/Spring_Fall]"); + +// reject +assert.throws(RangeError, () => dstStartDay.with(twoThirty, { + offset, + disambiguation: "reject" +})); +assert.throws(RangeError, () => dstEndDay.with(oneThirty, { + offset, + disambiguation: "reject" +})); + +// compatible is the default +assert.sameValue(`${ dstStartDay.with(twoThirty, { offset }) }`, `${ dstStartDay.with(twoThirty, { + offset, + disambiguation: "compatible" +}) }`); +assert.sameValue(`${ dstEndDay.with(twoThirty, { offset }) }`, `${ dstEndDay.with(twoThirty, { + offset, + disambiguation: "compatible" +}) }`); + +// invalid disambiguation +[ + "", + "EARLIER", + "balance" +].forEach(disambiguation => assert.throws(RangeError, () => zdt.with({ day: 5 }, { disambiguation }))); + +// Offset options +var bogus = { + ...twoThirty, + offset: "+23:59" +}; +// use, with bogus offset, changes to the exact time with the offset +var preserveExact = dstStartDay.with(bogus, { offset: "use" }); +assert.sameValue(`${ preserveExact }`, "2000-03-31T18:31:01-08:00[Custom/Spring_Fall]"); +assert.sameValue(preserveExact.epochNanoseconds, Temporal.Instant.from("2000-04-02T02:30:01+23:59").epochNanoseconds); + +// ignore, with bogus offset, defers to disambiguation option +var offset = "ignore"; +assert.sameValue(`${ dstStartDay.with(bogus, { + offset, + disambiguation: "earlier" +}) }`, "2000-04-02T01:30:01-08:00[Custom/Spring_Fall]"); +assert.sameValue(`${ dstStartDay.with(bogus, { + offset, + disambiguation: "later" +}) }`, "2000-04-02T03:30:01-07:00[Custom/Spring_Fall]"); + +// prefer, with bogus offset, defers to disambiguation option +var offset = "prefer"; +assert.sameValue(`${ dstStartDay.with(bogus, { + offset, + disambiguation: "earlier" +}) }`, "2000-04-02T01:30:01-08:00[Custom/Spring_Fall]"); +assert.sameValue(`${ dstStartDay.with(bogus, { + offset, + disambiguation: "later" +}) }`, "2000-04-02T03:30:01-07:00[Custom/Spring_Fall]"); + +// reject, with bogus offset, throws +assert.throws(RangeError, () => dstStartDay.with({ + ...twoThirty, + offset: "+23:59" +}, { offset: "reject" })); + +var doubleTime = new Temporal.ZonedDateTime(972811801_000_000_000n, dst); +// use changes to the exact time with the offset +var preserveExact = doubleTime.with({ offset: "-07:00" }, { offset: "use" }); +assert.sameValue(preserveExact.offset, "-07:00"); +assert.sameValue(preserveExact.epochNanoseconds, Temporal.Instant.from("2000-10-29T01:30:01-07:00").epochNanoseconds); + +// ignore defers to disambiguation option +var offset = "ignore"; +assert.sameValue(doubleTime.with({ offset: "-07:00" }, { + offset, + disambiguation: "earlier" +}).offset, "-07:00"); +assert.sameValue(doubleTime.with({ offset: "-07:00" }, { + offset, + disambiguation: "later" +}).offset, "-08:00"); + +// prefer adjusts offset of repeated clock time +assert.sameValue(doubleTime.with({ offset: "-07:00" }, { offset: "prefer" }).offset, "-07:00"); + +// reject adjusts offset of repeated clock time +assert.sameValue(doubleTime.with({ offset: "-07:00" }, { offset: "reject" }).offset, "-07:00"); + +// use does not cause the offset to change when adjusting repeated clock time +assert.sameValue(doubleTime.with({ minute: 31 }, { offset: "use" }).offset, "-08:00"); + +// ignore may cause the offset to change when adjusting repeated clock time +assert.sameValue(doubleTime.with({ minute: 31 }, { offset: "ignore" }).offset, "-07:00"); + +// prefer does not cause the offset to change when adjusting repeated clock time +assert.sameValue(doubleTime.with({ minute: 31 }, { offset: "prefer" }).offset, "-08:00"); + +// reject does not cause the offset to change when adjusting repeated clock time +assert.sameValue(doubleTime.with({ minute: 31 }, { offset: "reject" }).offset, "-08:00"); + +// prefer is the default +assert.sameValue(`${ dstStartDay.with(twoThirty) }`, `${ dstStartDay.with(twoThirty, { offset: "prefer" }) }`); +assert.sameValue(`${ dstEndDay.with(twoThirty) }`, `${ dstEndDay.with(twoThirty, { offset: "prefer" }) }`); +assert.sameValue(`${ doubleTime.with({ minute: 31 }) }`, `${ doubleTime.with({ minute: 31 }, { offset: "prefer" }) }`); + +// invalid offset +[ + "", + "PREFER", + "balance" +].forEach(offset => assert.throws(RangeError, () => zdt.with({ day: 5 }, { offset }))); + +// object must contain at least one correctly-spelled property +assert.throws(TypeError, () => zdt.with({})); +assert.throws(TypeError, () => zdt.with({ months: 12 })); + +// incorrectly-spelled properties are ignored +assert.sameValue(`${ zdt.with({ + month: 12, + days: 15 +}) }`, "1976-12-18T15:23:30.123456789+00:00[UTC]"); + +// throws if timeZone is included +assert.throws(TypeError, () => zdt.with({ + month: 2, + timeZone: "UTC" +})); + +// throws if given a Temporal object with a time zone +assert.throws(TypeError, () => zdt.with(dstStartDay)); + +// throws if calendarName is included +assert.throws(TypeError, () => zdt.with({ + month: 2, + calendar: "iso8601" +})); + +// throws if given a Temporal object with a calendar +assert.throws(TypeError, () => zdt.with(Temporal.PlainDateTime.from("1976-11-18T12:00"))); +assert.throws(TypeError, () => zdt.with(Temporal.PlainDate.from("1976-11-18"))); +assert.throws(TypeError, () => zdt.with(Temporal.PlainTime.from("12:00"))); +assert.throws(TypeError, () => zdt.with(Temporal.PlainYearMonth.from("1976-11"))); +assert.throws(TypeError, () => zdt.with(Temporal.PlainMonthDay.from("11-18"))); + +// throws if given a string +assert.throws(TypeError, () => zdt.with("1976-11-18T12:00+00:00[UTC]")); +assert.throws(TypeError, () => zdt.with("1976-11-18")); +assert.throws(TypeError, () => zdt.with("12:00")); +assert.throws(TypeError, () => zdt.with("invalid")); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withCalendar.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withCalendar.js new file mode 100644 index 0000000000..380cbeb664 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withCalendar.js @@ -0,0 +1,46 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.withCalendar() +features: [Temporal] +---*/ + +var zdt = Temporal.ZonedDateTime.from("2019-11-18T15:23:30.123456789-08:00[-08:00]"); + +// zonedDateTime.withCalendar(japanese) works +var cal = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "japanese", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +assert.sameValue(`${ zdt.withCalendar(cal) }`, "2019-11-18T15:23:30.123456789-08:00[-08:00][u-ca=japanese]"); + +// keeps instant and time zone the same +var zdt = Temporal.ZonedDateTime.from("2019-11-18T15:23:30.123456789+01:00[+01:00][u-ca=iso8601]"); +var zdt2 = zdt.withCalendar(cal); +assert.sameValue(zdt.epochNanoseconds, zdt2.epochNanoseconds); +assert.sameValue(zdt2.getCalendar(), cal); +assert.sameValue(zdt2.timeZoneId, "+01:00"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withPlainDate.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withPlainDate.js new file mode 100644 index 0000000000..810ebd3350 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withPlainDate.js @@ -0,0 +1,98 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: .withPlainDate manipulation +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var dst = TemporalHelpers.springForwardFallBackTimeZone(); +var zdt = Temporal.PlainDateTime.from("1995-12-07T03:24:30").toZonedDateTime(dst); + +// withPlainDate({ year: 2000, month: 6, day: 1 }) works +// and keeps wall time constant despite the UTC offset change +assert.sameValue(`${ zdt.withPlainDate({ + year: 2000, + month: 6, + day: 1 +}) }`, "2000-06-01T03:24:30-07:00[Custom/Spring_Fall]"); + +// withPlainDate(plainDate) works +var date = Temporal.PlainDate.from("2020-01-23"); +assert.sameValue(`${ zdt.withPlainDate(date) }`, "2020-01-23T03:24:30-08:00[Custom/Spring_Fall]"); + +// withPlainDate('2018-09-15') works +assert.sameValue(`${ zdt.withPlainDate("2018-09-15") }`, "2018-09-15T03:24:30-08:00[Custom/Spring_Fall]"); + +// result contains a non-ISO calendar if present in the input +var fakeJapanese = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "japanese", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +assert.sameValue(`${ zdt.withCalendar(fakeJapanese).withPlainDate("2008-09-06") }`, "2008-09-06T03:24:30-08:00[Custom/Spring_Fall][u-ca=japanese]"); + +// calendar is unchanged if input has ISO calendar +var date = new Temporal.PlainDate(2008, 9, 6, fakeJapanese); +assert.sameValue(`${ zdt.withPlainDate(date) }`, "2008-09-06T03:24:30-08:00[Custom/Spring_Fall][u-ca=japanese]"); + +// throws if both `this` and `other` have a non-ISO calendar +var fakeGregorian = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "gregory", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +assert.throws(RangeError, () => zdt.withCalendar(fakeGregorian).withPlainDate(date)); + +// object must contain at least one correctly-spelled property +assert.throws(TypeError, () => zdt.withPlainDate({})); +assert.throws(TypeError, () => zdt.withPlainDate({ months: 12 })); + +// incorrectly-spelled properties are ignored +assert.sameValue(`${ zdt.withPlainDate({ + year: 2000, + month: 6, + day: 1, + months: 123 +}) }`, "2000-06-01T03:24:30-07:00[Custom/Spring_Fall]"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withPlainTime.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withPlainTime.js new file mode 100644 index 0000000000..9bd03c02fd --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withPlainTime.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: .withPlainTime manipulation +features: [Temporal] +---*/ + +var zdt = Temporal.ZonedDateTime.from("2015-12-07T03:24:30.000003500[-08:00]"); + +// withPlainTime({ hour: 10 }) works +assert.sameValue(`${ zdt.withPlainTime({ hour: 10 }) }`, "2015-12-07T10:00:00-08:00[-08:00]"); + +// withPlainTime(time) works +var time = Temporal.PlainTime.from("11:22"); +assert.sameValue(`${ zdt.withPlainTime(time) }`, "2015-12-07T11:22:00-08:00[-08:00]"); + +// withPlainTime('12:34') works +assert.sameValue(`${ zdt.withPlainTime("12:34") }`, "2015-12-07T12:34:00-08:00[-08:00]"); + +// incorrectly-spelled properties are ignored +assert.sameValue(`${ zdt.withPlainTime({ + hour: 10, + seconds: 55 +}) }`, "2015-12-07T10:00:00-08:00[-08:00]"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withTimezone.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withTimezone.js new file mode 100644 index 0000000000..ae5951ff64 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withTimezone.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.withTimeZone() +features: [Temporal] +---*/ + +// keeps instant and calendar the same +var fakeGregorian = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "gregory", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +var zdt = Temporal.ZonedDateTime.from("2019-11-18T15:23:30.123456789+01:00[+01:00]").withCalendar(fakeGregorian); +var zdt2 = zdt.withTimeZone("-08:00"); +assert.sameValue(zdt.epochNanoseconds, zdt2.epochNanoseconds); +assert.sameValue(zdt2.getCalendar(), fakeGregorian); +assert.sameValue(zdt2.timeZoneId, "-08:00"); +assert.notSameValue(`${ zdt.toPlainDateTime() }`, `${ zdt2.toPlainDateTime() }`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/shell.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/shell.js diff --git a/js/src/tests/test262/staging/Temporal/browser.js b/js/src/tests/test262/staging/Temporal/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/browser.js diff --git a/js/src/tests/test262/staging/Temporal/shell.js b/js/src/tests/test262/staging/Temporal/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/shell.js |