diff options
Diffstat (limited to 'js/src/tests/test262/staging/Temporal/ZonedDateTime/old')
28 files changed, 5085 insertions, 0 deletions
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); |