From a3040a5047694f37b793c4394fd9cfa591dd17c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Tue, 5 Jul 2022 12:48:26 +0200 Subject: [PATCH] Import SpiderMonkey Temporal tests Temporal tests written for the SpiderMonkey implementation. Mostly covers edge cases around mathematical operations and regression tests for reported spec bugs. --- ...ation-years-and-months-number-max-value.js | 33 +++++++ ...-is-number-max-value-with-zoneddatetime.js | 18 ++++ .../prototype/add/days-is-number-max-value.js | 13 +++ .../add/nanoseconds-is-number-max-value-1.js | 27 ++++++ ...nanoseconds-to-days-loop-indefinitely-1.js | 72 ++++++++++++++++ ...nanoseconds-to-days-loop-indefinitely-2.js | 65 ++++++++++++++ .../round/number-max-value-too-large.js | 27 ++++++ ...en-sign-mismatched-with-zoned-date-time.js | 48 +++++++++++ ...-duration-relative-when-sign-mismatched.js | 33 +++++++ ...noseconds-too-large-with-zoned-datetime.js | 23 +++++ ...-is-number-max-value-with-zoneddatetime.js | 19 +++++ .../subtract/days-is-number-max-value.js | 14 +++ .../nanoseconds-is-number-max-value-1.js | 28 ++++++ .../precision-formatted-as-decimal-number.js | 44 ++++++++++ ...throws-when-rounded-duration-is-invalid.js | 45 ++++++++++ ...relativeto-string-plaindatetime-invalid.js | 23 +++++ ...ime-with-fractional-days-different-sign.js | 35 ++++++++ ...veto-zoneddatetime-with-fractional-days.js | 24 ++++++ ...with-offset-not-valid-epoch-nanoseconds.js | 20 +++++ .../balance-infinite-nanoseconds-duration.js | 45 ++++++++++ ...ible-instants-with-datetime-near-limits.js | 30 +++++++ .../plain-date-time-near-limits.js | 34 ++++++++ .../balance-infinite-nanoseconds-duration.js | 45 ++++++++++ .../add/argument-string-duration-too-large.js | 18 ++++ .../argument-string-duration-too-large.js | 18 ++++ .../fixed-offset-near-date-time-limits.js | 54 ++++++++++++ .../smallest-unit-day-daylength-too-large.js | 85 +++++++++++++++++++ ...est-unit-day-daylength-zero-or-negative.js | 67 +++++++++++++++ .../minimum-instant-with-one-hour-offset.js | 30 +++++++ ...e-like-at-minimum-date-time-with-offset.js | 39 +++++++++ ...oned-datetime-like-at-minimum-date-time.js | 34 ++++++++ ...ond-and-nanosecond-from-last-transition.js | 55 ++++++++++++ ...s-subtracted-or-added-at-dst-transition.js | 24 ++++++ .../Temporal/TimeZone/supported-values-of.js | 16 ++++ 34 files changed, 1205 insertions(+) create mode 100644 test/built-ins/Temporal/Calendar/prototype/dateAdd/argument-duration-years-and-months-number-max-value.js create mode 100644 test/built-ins/Temporal/Duration/prototype/add/days-is-number-max-value-with-zoneddatetime.js create mode 100644 test/built-ins/Temporal/Duration/prototype/add/days-is-number-max-value.js create mode 100644 test/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-value-1.js create mode 100644 test/built-ins/Temporal/Duration/prototype/round/nanoseconds-to-days-loop-indefinitely-1.js create mode 100644 test/built-ins/Temporal/Duration/prototype/round/nanoseconds-to-days-loop-indefinitely-2.js create mode 100644 test/built-ins/Temporal/Duration/prototype/round/number-max-value-too-large.js create mode 100644 test/built-ins/Temporal/Duration/prototype/round/throws-in-balance-duration-when-sign-mismatched-with-zoned-date-time.js create mode 100644 test/built-ins/Temporal/Duration/prototype/round/throws-in-unbalance-duration-relative-when-sign-mismatched.js create mode 100644 test/built-ins/Temporal/Duration/prototype/round/total-duration-nanoseconds-too-large-with-zoned-datetime.js create mode 100644 test/built-ins/Temporal/Duration/prototype/subtract/days-is-number-max-value-with-zoneddatetime.js create mode 100644 test/built-ins/Temporal/Duration/prototype/subtract/days-is-number-max-value.js create mode 100644 test/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-value-1.js create mode 100644 test/built-ins/Temporal/Duration/prototype/toString/precision-formatted-as-decimal-number.js create mode 100644 test/built-ins/Temporal/Duration/prototype/toString/throws-when-rounded-duration-is-invalid.js create mode 100644 test/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime-invalid.js create mode 100644 test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days-different-sign.js create mode 100644 test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days.js create mode 100644 test/built-ins/Temporal/Instant/compare/argument-string-with-offset-not-valid-epoch-nanoseconds.js create mode 100644 test/built-ins/Temporal/PlainDateTime/prototype/since/balance-infinite-nanoseconds-duration.js create mode 100644 test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguate-empty-possible-instants-with-datetime-near-limits.js create mode 100644 test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-date-time-near-limits.js create mode 100644 test/built-ins/Temporal/PlainDateTime/prototype/until/balance-infinite-nanoseconds-duration.js create mode 100644 test/built-ins/Temporal/PlainTime/prototype/add/argument-string-duration-too-large.js create mode 100644 test/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-duration-too-large.js create mode 100644 test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/fixed-offset-near-date-time-limits.js create mode 100644 test/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-daylength-too-large.js create mode 100644 test/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-daylength-zero-or-negative.js create mode 100644 test/built-ins/Temporal/ZonedDateTime/prototype/with/minimum-instant-with-one-hour-offset.js create mode 100644 test/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time-with-offset.js create mode 100644 test/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time.js create mode 100644 test/intl402/Temporal/TimeZone/prototype/getNextTransition/subtract-second-and-nanosecond-from-last-transition.js create mode 100644 test/intl402/Temporal/TimeZone/prototype/getPreviousTransition/nanoseconds-subtracted-or-added-at-dst-transition.js create mode 100644 test/intl402/Temporal/TimeZone/supported-values-of.js diff --git a/test/built-ins/Temporal/Calendar/prototype/dateAdd/argument-duration-years-and-months-number-max-value.js b/test/built-ins/Temporal/Calendar/prototype/dateAdd/argument-duration-years-and-months-number-max-value.js new file mode 100644 index 0000000000..3befbb4352 --- /dev/null +++ b/test/built-ins/Temporal/Calendar/prototype/dateAdd/argument-duration-years-and-months-number-max-value.js @@ -0,0 +1,33 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.calendar.prototype.dateadd +description: > + Call BalanceISOYearMonth with Number.MAX_VALUE and -Number.MAX_VALUE for years/months. +info: | + Temporal.Calendar.prototype.dateAdd ( date, duration [ , options ] ) + + ... + 9. Let result be ? AddISODate(date.[[ISOYear]], date.[[ISOMonth]], date.[[ISODay]], + duration.[[Years]], duration.[[Months]], duration.[[Weeks]], balanceResult.[[Days]], + overflow). + 10. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar). + + AddISODate ( year, month, day, years, months, weeks, days, overflow ) + + ... + 3. Let intermediate be ! BalanceISOYearMonth(year + years, month + months). + ... + +features: [Temporal] +---*/ + +var cal = new Temporal.Calendar("iso8601"); +var date = new Temporal.PlainDate(1970, 1, 1); + +var maxValue = new Temporal.Duration(Number.MAX_VALUE, Number.MAX_VALUE); +var minValue = new Temporal.Duration(-Number.MAX_VALUE, -Number.MAX_VALUE); + +assert.throws(RangeError, () => cal.dateAdd(date, maxValue), "years/months is +Number.MAX_VALUE"); +assert.throws(RangeError, () => cal.dateAdd(date, minValue), "years/months is -Number.MAX_VALUE"); diff --git a/test/built-ins/Temporal/Duration/prototype/add/days-is-number-max-value-with-zoneddatetime.js b/test/built-ins/Temporal/Duration/prototype/add/days-is-number-max-value-with-zoneddatetime.js new file mode 100644 index 0000000000..7f146c6ebb --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/add/days-is-number-max-value-with-zoneddatetime.js @@ -0,0 +1,18 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: > + AddZonedDateTime throws a RangeError when the intermediate instant is too large. +features: [Temporal] +---*/ + +const plainDate = new Temporal.PlainDate(1970, 1, 1); +const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601"); + +var duration = Temporal.Duration.from({days: 1, nanoseconds: Number.MAX_VALUE}); + +var options = {relativeTo: zonedDateTime}; + +assert.throws(RangeError, () => duration.add(duration, options)); diff --git a/test/built-ins/Temporal/Duration/prototype/add/days-is-number-max-value.js b/test/built-ins/Temporal/Duration/prototype/add/days-is-number-max-value.js new file mode 100644 index 0000000000..a6621b48c8 --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/add/days-is-number-max-value.js @@ -0,0 +1,13 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: > + BalanceDuration throws a RangeError when the result is too large. +features: [Temporal] +---*/ + +var duration = Temporal.Duration.from({days: Number.MAX_VALUE}); + +assert.throws(RangeError, () => duration.add(duration)); diff --git a/test/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-value-1.js b/test/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-value-1.js new file mode 100644 index 0000000000..db02d1dddd --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-value-1.js @@ -0,0 +1,27 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: > + BalanceDuration throws a RangeError when the result is too large. +features: [Temporal] +---*/ + +const plainDate = new Temporal.PlainDate(1970, 1, 1); +const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601"); + +// Largest temporal unit is "nanosecond". +const duration = Temporal.Duration.from({nanoseconds: Number.MAX_VALUE}); + +assert.throws(RangeError, () => { + duration.add(duration); +}); + +assert.throws(RangeError, () => { + duration.add(duration, {relativeTo: plainDate}); +}); + +assert.throws(RangeError, () => { + duration.add(duration, {relativeTo: zonedDateTime}); +}); diff --git a/test/built-ins/Temporal/Duration/prototype/round/nanoseconds-to-days-loop-indefinitely-1.js b/test/built-ins/Temporal/Duration/prototype/round/nanoseconds-to-days-loop-indefinitely-1.js new file mode 100644 index 0000000000..d474f5072b --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/round/nanoseconds-to-days-loop-indefinitely-1.js @@ -0,0 +1,72 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: > + NanosecondsToDays can loop indefinitely. +info: | + NanosecondsToDays ( nanoseconds, relativeTo ) + + ... + 15. If sign is 1, then + a. Repeat, while days > 0 and intermediateNs > endNs, + i. Set days to days - 1. + ii. Set intermediateNs to ℝ(? AddZonedDateTime(ℤ(startNs), relativeTo.[[TimeZone]], + relativeTo.[[Calendar]], 0, 0, 0, days, 0, 0, 0, 0, 0, 0)). + ... +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// Intentionally not Test262Error to ensure assertion errors are correctly propagated. +class StopExecution extends Error {} + +const stopAt = 1000; + +// Always add two days to the start date, this ensures the intermediate date +// is larger than the end date. +let twoDays = Temporal.Duration.from({days: 2}); + +// Number of calls to dateAdd. +let count = 0; + +let cal = new class extends Temporal.Calendar { + // Set `days` to a number larger than `Number.MAX_SAFE_INTEGER`. + dateUntil(start, end, options) { + return Temporal.Duration.from({days: Number.MAX_VALUE}); + } + + dateAdd(date, duration, options) { + // Stop when we've reached the test limit. + count += 1; + if (count === stopAt) { + throw new StopExecution(); + } + + if (count === 1) { + return Temporal.Calendar.prototype.dateAdd.call(this, date, duration, options); + } + + TemporalHelpers.assertPlainDate(date, 1970, 1, "M01", 1); + + TemporalHelpers.assertDuration( + duration, + 0, 0, 0, Number.MAX_VALUE, + 0, 0, 0, + 0, 0, 0, + ); + + return Temporal.Calendar.prototype.dateAdd.call(this, date, twoDays, options); + } +}("iso8601"); + +let zdt = new Temporal.ZonedDateTime(0n, "UTC", cal); +let duration = Temporal.Duration.from({days: 1}); +let options = { + largestUnit: "days", + relativeTo: zdt, +}; + +assert.throws(StopExecution, () => duration.round(options)); +assert.sameValue(count, stopAt); diff --git a/test/built-ins/Temporal/Duration/prototype/round/nanoseconds-to-days-loop-indefinitely-2.js b/test/built-ins/Temporal/Duration/prototype/round/nanoseconds-to-days-loop-indefinitely-2.js new file mode 100644 index 0000000000..15badc7eff --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/round/nanoseconds-to-days-loop-indefinitely-2.js @@ -0,0 +1,65 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: > + NanosecondsToDays can loop indefinitely. +info: | + NanosecondsToDays ( nanoseconds, relativeTo ) + + ... + 18. Repeat, while done is false, + a. Let oneDayFartherNs be ℝ(? AddZonedDateTime(ℤ(intermediateNs), relativeTo.[[TimeZone]], + relativeTo.[[Calendar]], 0, 0, 0, sign, 0, 0, 0, 0, 0, 0)). + b. Set dayLengthNs to oneDayFartherNs - intermediateNs. + c. If (nanoseconds - dayLengthNs) × sign ≥ 0, then + i. Set nanoseconds to nanoseconds - dayLengthNs. + ii. Set intermediateNs to oneDayFartherNs. + iii. Set days to days + sign. + d. Else, + i. Set done to true. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// Intentionally not Test262Error to ensure assertion errors are correctly propagated. +class StopExecution extends Error {} + +const stopAt = 1000; + +// Number of calls to getPossibleInstantsFor. +let count = 0; + +// UTC time zones so we don't have to worry about time zone offsets. +let tz = new class extends Temporal.TimeZone { + getPossibleInstantsFor(dt) { + // Stop when we've reached the test limit. + count += 1; + if (count === stopAt) { + throw new StopExecution(); + } + + if (count < 4) { + // The first couple calls request the instant for 1970-01-02. + TemporalHelpers.assertPlainDateTime(dt, 1970, 1, "M01", 2, 0, 0, 0, 0, 0, 0); + } else { + // Later on the instant for 1970-01-03 is requested. + TemporalHelpers.assertPlainDateTime(dt, 1970, 1, "M01", 3, 0, 0, 0, 0, 0, 0); + } + + // Always return "1970-01-02T00:00:00Z". This leads to iterating indefinitely + // in NanosecondsToDays. + return [new Temporal.Instant(86400000000000n)]; + } +}("UTC"); + +let zdt = new Temporal.ZonedDateTime(0n, tz); +let duration = Temporal.Duration.from({days: 1}); +let options = { + smallestUnit: "days", + relativeTo: zdt, +}; + +assert.throws(StopExecution, () => duration.round(options)); +assert.sameValue(count, stopAt); diff --git a/test/built-ins/Temporal/Duration/prototype/round/number-max-value-too-large.js b/test/built-ins/Temporal/Duration/prototype/round/number-max-value-too-large.js new file mode 100644 index 0000000000..62338b043e --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/round/number-max-value-too-large.js @@ -0,0 +1,27 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: > + RoundDuration throws a RangeError when the result duration is invalid. +features: [Temporal] +---*/ + +function test(unit, nextSmallestUnit) { + var duration = Temporal.Duration.from({ + [unit]: Number.MAX_VALUE, + [nextSmallestUnit]: Number.MAX_VALUE, + }); + + var options = {smallestUnit: unit, largestUnit: unit}; + + assert.throws(RangeError, () => duration.round(options)); +} + +test("days", "hours"); +test("hours", "minutes"); +test("minutes", "seconds"); +test("seconds", "milliseconds"); +test("milliseconds", "microseconds"); +test("microseconds", "nanoseconds"); diff --git a/test/built-ins/Temporal/Duration/prototype/round/throws-in-balance-duration-when-sign-mismatched-with-zoned-date-time.js b/test/built-ins/Temporal/Duration/prototype/round/throws-in-balance-duration-when-sign-mismatched-with-zoned-date-time.js new file mode 100644 index 0000000000..40a83323fd --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/round/throws-in-balance-duration-when-sign-mismatched-with-zoned-date-time.js @@ -0,0 +1,48 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: > + BalanceDuration throws when the duration signs don't match. +info: | + BalanceDuration ( days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, + largestUnit [ , relativeTo ] ) + + ... + 4. If largestUnit is one of "year", "month", "week", or "day", then + a. Let result be ? NanosecondsToDays(nanoseconds, relativeTo). + b. Set days to result.[[Days]]. + c. Set nanoseconds to result.[[Nanoseconds]]. + ... + 15. Return ? CreateTimeDurationRecord(days, hours × sign, minutes × sign, seconds × sign, + milliseconds × sign, microseconds × sign, nanoseconds × sign). +features: [Temporal] +---*/ + +let duration = Temporal.Duration.from({ + hours: -(24 * 1), + nanoseconds: -1, +}); + +let tz = new class extends Temporal.TimeZone { + #getPossibleInstantsFor = 0; + + getPossibleInstantsFor(dt) { + this.#getPossibleInstantsFor++; + + if (this.#getPossibleInstantsFor === 1) { + return [new Temporal.Instant(-86400_000_000_000n - 2n)] + } + return super.getPossibleInstantsFor(dt); + } +}("UTC"); + +let zdt = new Temporal.ZonedDateTime(0n, tz, "iso8601"); + +let options = { + relativeTo: zdt, + largestUnit: "days", +}; + +assert.throws(RangeError, () => duration.round(options)); diff --git a/test/built-ins/Temporal/Duration/prototype/round/throws-in-unbalance-duration-relative-when-sign-mismatched.js b/test/built-ins/Temporal/Duration/prototype/round/throws-in-unbalance-duration-relative-when-sign-mismatched.js new file mode 100644 index 0000000000..2cac32ec24 --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/round/throws-in-unbalance-duration-relative-when-sign-mismatched.js @@ -0,0 +1,33 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: > + UnbalanceDurationRelative throws a RangeError when duration signs don't match. +features: [Temporal] +---*/ + +var duration = Temporal.Duration.from({ + years: 1, + months: 0, + weeks: 1, +}); + +var cal = new class extends Temporal.Calendar { + dateUntil(one, two, options) { + var result = super.dateUntil(one, two, options); + return result.negated(); + } +}("iso8601"); + +var relativeTo = new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, 0, cal); +assert.sameValue(relativeTo.calendar, cal); + +var options = { + smallestUnit: "days", + largestUnit: "month", + relativeTo, +}; + +assert.throws(RangeError, () => duration.round(options)); diff --git a/test/built-ins/Temporal/Duration/prototype/round/total-duration-nanoseconds-too-large-with-zoned-datetime.js b/test/built-ins/Temporal/Duration/prototype/round/total-duration-nanoseconds-too-large-with-zoned-datetime.js new file mode 100644 index 0000000000..76a6510d56 --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/round/total-duration-nanoseconds-too-large-with-zoned-datetime.js @@ -0,0 +1,23 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: > + NanosecondsToDays throws a RangeError when the number of nanoseconds is too large. +features: [Temporal] +---*/ + +var duration = Temporal.Duration.from({ + nanoseconds: Number.MAX_VALUE, +}); + +var zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC"); + +var options = { + smallestUnit: "day", + largestUnit: "day", + relativeTo: zonedDateTime, +}; + +assert.throws(RangeError, () => duration.round(options)); diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/days-is-number-max-value-with-zoneddatetime.js b/test/built-ins/Temporal/Duration/prototype/subtract/days-is-number-max-value-with-zoneddatetime.js new file mode 100644 index 0000000000..d84cb057f9 --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/subtract/days-is-number-max-value-with-zoneddatetime.js @@ -0,0 +1,19 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: > + AddZonedDateTime throws a RangeError when the intermediate instant is too large. +features: [Temporal] +---*/ + +const plainDate = new Temporal.PlainDate(1970, 1, 1); +const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601"); + +var duration1 = Temporal.Duration.from({days: 1, nanoseconds: Number.MAX_VALUE}); +var duration2 = Temporal.Duration.from({days: -1, nanoseconds: -Number.MAX_VALUE}); + +var options = {relativeTo: zonedDateTime}; + +assert.throws(RangeError, () => duration1.subtract(duration2, options)); diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/days-is-number-max-value.js b/test/built-ins/Temporal/Duration/prototype/subtract/days-is-number-max-value.js new file mode 100644 index 0000000000..365eb71c0b --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/subtract/days-is-number-max-value.js @@ -0,0 +1,14 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: > + BalanceDuration throws a RangeError when the result is too large. +features: [Temporal] +---*/ + +var duration1 = Temporal.Duration.from({days: Number.MAX_VALUE}); +var duration2 = Temporal.Duration.from({days: -Number.MAX_VALUE}); + +assert.throws(RangeError, () => duration1.subtract(duration2)); diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-value-1.js b/test/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-value-1.js new file mode 100644 index 0000000000..50ca5cc2e8 --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-value-1.js @@ -0,0 +1,28 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: > + BalanceDuration throws a RangeError when the result is too large. +features: [Temporal] +---*/ + +const plainDate = new Temporal.PlainDate(1970, 1, 1); +const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601"); + +// Largest temporal unit is "nanosecond". +const duration1 = Temporal.Duration.from({nanoseconds: Number.MAX_VALUE}); +const duration2 = Temporal.Duration.from({nanoseconds: -Number.MAX_VALUE}); + +assert.throws(RangeError, () => { + duration1.subtract(duration2); +}); + +assert.throws(RangeError, () => { + duration1.subtract(duration2, {relativeTo: plainDate}); +}); + +assert.throws(RangeError, () => { + duration1.subtract(duration2, {relativeTo: zonedDateTime}); +}); diff --git a/test/built-ins/Temporal/Duration/prototype/toString/precision-formatted-as-decimal-number.js b/test/built-ins/Temporal/Duration/prototype/toString/precision-formatted-as-decimal-number.js new file mode 100644 index 0000000000..b546b6aabc --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/toString/precision-formatted-as-decimal-number.js @@ -0,0 +1,44 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-temporaldurationtostring +description: > + Duration components are formatted as precise decimal numbers. +info: | + TemporalDurationToString ( years, months, weeks, days, hours, minutes, seconds, milliseconds, + microseconds, nanoseconds, precision ) + ... + 9. If years is not 0, then + a. Set datePart to the string concatenation of abs(years) formatted as a decimal number and + the code unit 0x0059 (LATIN CAPITAL LETTER Y). + 10. If months is not 0, then + a. Set datePart to the string concatenation of datePart, abs(months) formatted as a decimal + number, and the code unit 0x004D (LATIN CAPITAL LETTER M). + If weeks is not 0, then + a. Set datePart to the string concatenation of datePart, abs(weeks) formatted as a decimal + number, and the code unit 0x0057 (LATIN CAPITAL LETTER W). + ... +features: [Temporal] +---*/ + +{ + let d = Temporal.Duration.from({weeks: 10000000000000004000}); + + // Number(10000000000000004000).toString() is "10000000000000004000". + assert.sameValue(d.toString(), "P10000000000000004096W"); +} + +{ + let d = Temporal.Duration.from({months: 9e59}); + + // Number(9e+59).toString() is "9e+59". + assert.sameValue(d.toString(), "P899999999999999918767229449717619953810131273674690656206848M"); +} + +{ + let d = Temporal.Duration.from({years: Number.MAX_VALUE}); + + // Number.MAX_VALUE.toString() is "1.7976931348623157e+308". + assert.sameValue(d.toString(), "P179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368Y"); +} diff --git a/test/built-ins/Temporal/Duration/prototype/toString/throws-when-rounded-duration-is-invalid.js b/test/built-ins/Temporal/Duration/prototype/toString/throws-when-rounded-duration-is-invalid.js new file mode 100644 index 0000000000..3a01b700e3 --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/toString/throws-when-rounded-duration-is-invalid.js @@ -0,0 +1,45 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: > + RoundDuration throws when the rounded duration can't be represented using + float64-representable integers. +info: | + Temporal.Duration.prototype.toString ( [ options ] ) + + ... + 7. Let result be (? RoundDuration(...)).[[DurationRecord]]. + ... + + RoundDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, + nanoseconds, increment, unit, roundingMode [ , relativeTo ] ) + + ... + 15. Else if unit is "second", then + a. Set seconds to RoundNumberToIncrement(fractionalSeconds, increment, roundingMode). + b. Set remainder to fractionalSeconds - seconds. + c. Set milliseconds, microseconds, and nanoseconds to 0. + ... + 19. Let duration be ? CreateDurationRecord(years, months, weeks, days, hours, minutes, seconds, + milliseconds, microseconds, nanoseconds). + ... + + CreateDurationRecord ( years, months, weeks, days, hours, minutes, seconds, milliseconds, + microseconds, nanoseconds ) + + 1. If ! IsValidDuration(years, months, weeks, days, hours, minutes, seconds, milliseconds, + microseconds, nanoseconds) is false, throw a RangeError exception. + ... +features: [Temporal] +---*/ + +var duration = Temporal.Duration.from({ + seconds: Number.MAX_VALUE, + milliseconds: Number.MAX_VALUE, +}); + +var options = {smallestUnit: "seconds"}; + +assert.throws(RangeError, () => duration.toString(options)); diff --git a/test/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime-invalid.js b/test/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime-invalid.js new file mode 100644 index 0000000000..c54783bd29 --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime-invalid.js @@ -0,0 +1,23 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: > + Throws a RangeError if "relativeTo" is a date/time value outside the valid limits. +info: | + Temporal.Duration.prototype.total ( totalOf ) + ... + 6. Let relativeTo be ? ToRelativeTemporalObject(totalOf). + ... + + ToRelativeTemporalObject ( options ) + ... + 9. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar). +features: [Temporal] +---*/ + +var duration = Temporal.Duration.from({nanoseconds: 0}); +var options = {unit: "nanoseconds", relativeTo: "+999999-01-01"}; + +assert.throws(RangeError, () => duration.total(options)); diff --git a/test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days-different-sign.js b/test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days-different-sign.js new file mode 100644 index 0000000000..b9730252be --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days-different-sign.js @@ -0,0 +1,35 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: > + Relative to a ZonedDateTime with a fractional number of days and different sign. +features: [Temporal] +---*/ + +let duration = Temporal.Duration.from({ + weeks: 1, + days: 0, + hours: 1, +}); + +let cal = new class extends Temporal.Calendar { + #dateAdd = 0; + + dateAdd(date, duration, options) { + if (++this.#dateAdd === 1) { + duration = "-P1W"; + } + return super.dateAdd(date, duration, options); + } +}("iso8601"); + +let zdt = new Temporal.ZonedDateTime(0n, "UTC", cal); + +let result = duration.total({ + relativeTo: zdt, + unit: "days", +}); + +assert.sameValue(result, -7 + 1 / 24); diff --git a/test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days.js b/test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days.js new file mode 100644 index 0000000000..48d726d937 --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days.js @@ -0,0 +1,24 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: > + Relative to a ZonedDateTime with a fractional number of days. +features: [Temporal] +---*/ + +let duration = Temporal.Duration.from({ + weeks: 1, + days: 0, + hours: 1, +}); + +let zdt = new Temporal.ZonedDateTime(0n, "UTC", "iso8601"); + +let result = duration.total({ + relativeTo: zdt, + unit: "days", +}); + +assert.sameValue(result, 7 + 1 / 24); diff --git a/test/built-ins/Temporal/Instant/compare/argument-string-with-offset-not-valid-epoch-nanoseconds.js b/test/built-ins/Temporal/Instant/compare/argument-string-with-offset-not-valid-epoch-nanoseconds.js new file mode 100644 index 0000000000..7c1419f076 --- /dev/null +++ b/test/built-ins/Temporal/Instant/compare/argument-string-with-offset-not-valid-epoch-nanoseconds.js @@ -0,0 +1,20 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.instant.compare +description: > + Throws when argument at maximum representable date/time value with a negative offset. +features: [Temporal] +---*/ + +// Not a valid epoch nanoseconds value due to the offset. +var one = "+275760-09-13T00:00:00.000-12"; + +var two = { + toString() { + throw new Test262Error(); + } +}; + +assert.throws(RangeError, () => Temporal.Instant.compare(one, two)); diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/balance-infinite-nanoseconds-duration.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/balance-infinite-nanoseconds-duration.js new file mode 100644 index 0000000000..aa20754c39 --- /dev/null +++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/balance-infinite-nanoseconds-duration.js @@ -0,0 +1,45 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.since +description: > + BalanceDuration throws when the result duration is invalid. +info: | + DifferenceISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, + y2, mon2, d2, h2, min2, s2, ms2, mus2, ns2, + calendar, largestUnit, options ) + + ... + 12. Let dateDifference be ? CalendarDateUntil(calendar, date1, date2, untilOptions). + 13. Let balanceResult be ? BalanceDuration(dateDifference.[[Days]], timeDifference.[[Hours]], + timeDifference.[[Minutes]], timeDifference.[[Seconds]], timeDifference.[[Milliseconds]], + timeDifference.[[Microseconds]], timeDifference.[[Nanoseconds]], largestUnit). + ... + + BalanceDuration ( days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, + largestUnit [ , relativeTo ] ) + + ... + 3. Else, + a. Set nanoseconds to ! TotalDurationNanoseconds(days, hours, minutes, seconds, milliseconds, + microseconds, nanoseconds, 0). + ... + 15. Return ? CreateTimeDurationRecord(days, hours × sign, minutes × sign, seconds × sign, + milliseconds × sign, microseconds × sign, nanoseconds × sign). +features: [Temporal] +---*/ + +var cal = new class extends Temporal.Calendar { + dateUntil(date1, date2, options) { + return Temporal.Duration.from({days: Number.MAX_VALUE}); + } +}("iso8601"); + +var dt1 = new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, 0, cal); +var dt2 = new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, 0, cal); +var options = {largestUnit: "nanoseconds"}; + +assert.throws(RangeError, () => dt1.since(dt1, options)); +assert.throws(RangeError, () => dt1.since(dt2, options)); +assert.throws(RangeError, () => dt2.since(dt1, options)); diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguate-empty-possible-instants-with-datetime-near-limits.js b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguate-empty-possible-instants-with-datetime-near-limits.js new file mode 100644 index 0000000000..22d59e5e18 --- /dev/null +++ b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/disambiguate-empty-possible-instants-with-datetime-near-limits.js @@ -0,0 +1,30 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.tozoneddatetime +description: > + Throws when at minimum resp. maximum value and possible instants is an empty List. +info: | + DisambiguatePossibleInstants ( possibleInstants, timeZone, dateTime, disambiguation ) + + ... + 9. If ! IsValidEpochNanoseconds(dayBeforeNs) is false, throw a RangeError exception. + ... + 12. If ! IsValidEpochNanoseconds(dayAfterNs) is false, throw a RangeError exception. + ... +features: [Temporal] +---*/ + +class TZ extends Temporal.TimeZone { + getPossibleInstantsFor() { + return []; + } +} + +var tz = new TZ("UTC"); +var min = new Temporal.PlainDateTime(-271821, 4, 20); +var max = new Temporal.PlainDateTime(275760, 9, 13); + +assert.throws(RangeError, () => min.toZonedDateTime(tz), "minimum date-time"); +assert.throws(RangeError, () => max.toZonedDateTime(tz), "maximum date-time"); diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-date-time-near-limits.js b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-date-time-near-limits.js new file mode 100644 index 0000000000..cc25c3b7a7 --- /dev/null +++ b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/plain-date-time-near-limits.js @@ -0,0 +1,34 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.tozoneddatetime +description: > + Throws a RangeError if the date/time value is outside the instant limits +info: | + Temporal.PlainDateTime.prototype.toZonedDateTime ( temporalTimeZoneLike [ , options ] ) + ... + 6. Let instant be ? BuiltinTimeZoneGetInstantFor(timeZone, dateTime, disambiguation). + ... +features: [Temporal] +---*/ + +// Try to create from the minimum date-time. +{ + let dt = new Temporal.PlainDateTime(-271821, 4, 19, 0, 0, 0, 0, 0, 1); + assert.throws(RangeError, () => dt.toZonedDateTime("UTC")); +} +{ + let dt = new Temporal.PlainDateTime(-271821, 4, 19, 1, 0, 0, 0, 0, 0); + assert.throws(RangeError, () => dt.toZonedDateTime("UTC")); +} + +// Try to create from the maximum date-time. +{ + let dt = new Temporal.PlainDateTime(275760, 9, 13, 0, 0, 0, 0, 0, 1); + assert.throws(RangeError, () => dt.toZonedDateTime("UTC")); +} +{ + let dt = new Temporal.PlainDateTime(275760, 9, 13, 1, 0, 0, 0, 0, 0); + assert.throws(RangeError, () => dt.toZonedDateTime("UTC")); +} diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/balance-infinite-nanoseconds-duration.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/balance-infinite-nanoseconds-duration.js new file mode 100644 index 0000000000..7a5c86b1f7 --- /dev/null +++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/balance-infinite-nanoseconds-duration.js @@ -0,0 +1,45 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.since +description: > + BalanceDuration throws when the result duration is invalid. +info: | + DifferenceISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, + y2, mon2, d2, h2, min2, s2, ms2, mus2, ns2, + calendar, largestUnit, options ) + + ... + 12. Let dateDifference be ? CalendarDateUntil(calendar, date1, date2, untilOptions). + 13. Let balanceResult be ? BalanceDuration(dateDifference.[[Days]], timeDifference.[[Hours]], + timeDifference.[[Minutes]], timeDifference.[[Seconds]], timeDifference.[[Milliseconds]], + timeDifference.[[Microseconds]], timeDifference.[[Nanoseconds]], largestUnit). + ... + + BalanceDuration ( days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, + largestUnit [ , relativeTo ] ) + + ... + 3. Else, + a. Set nanoseconds to ! TotalDurationNanoseconds(days, hours, minutes, seconds, milliseconds, + microseconds, nanoseconds, 0). + ... + 15. Return ? CreateTimeDurationRecord(days, hours × sign, minutes × sign, seconds × sign, + milliseconds × sign, microseconds × sign, nanoseconds × sign). +features: [Temporal] +---*/ + +var cal = new class extends Temporal.Calendar { + dateUntil(date1, date2, options) { + return Temporal.Duration.from({days: Number.MAX_VALUE}); + } +}("iso8601"); + +var dt1 = new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, 0, cal); +var dt2 = new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, 0, cal); +var options = {largestUnit: "nanoseconds"}; + +assert.throws(RangeError, () => dt1.until(dt1, options)); +assert.throws(RangeError, () => dt1.until(dt2, options)); +assert.throws(RangeError, () => dt2.until(dt1, options)); diff --git a/test/built-ins/Temporal/PlainTime/prototype/add/argument-string-duration-too-large.js b/test/built-ins/Temporal/PlainTime/prototype/add/argument-string-duration-too-large.js new file mode 100644 index 0000000000..2b12e4aad5 --- /dev/null +++ b/test/built-ins/Temporal/PlainTime/prototype/add/argument-string-duration-too-large.js @@ -0,0 +1,18 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaintime.prototype.add +description: > + ParseTemporalDurationString throws a RangeError when the result is too large. +features: [Temporal] +---*/ + +// Number string too long to be representable as a Number value. +var ones = "1".repeat(1000); +assert.sameValue(Number(ones), Infinity); + +var time = new Temporal.PlainTime(); +var str = "PT" + ones + "S"; + +assert.throws(RangeError, () => time.add(str)); diff --git a/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-duration-too-large.js b/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-duration-too-large.js new file mode 100644 index 0000000000..353f14c3ad --- /dev/null +++ b/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-duration-too-large.js @@ -0,0 +1,18 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaintime.prototype.subtract +description: > + ParseTemporalDurationString throws a RangeError when the result is too large. +features: [Temporal] +---*/ + +// Number string too long to be representable as a Number value. +var ones = "1".repeat(1000); +assert.sameValue(Number(ones), Infinity); + +var time = new Temporal.PlainTime(); +var str = "PT" + ones + "S"; + +assert.throws(RangeError, () => time.subtract(str)); diff --git a/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/fixed-offset-near-date-time-limits.js b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/fixed-offset-near-date-time-limits.js new file mode 100644 index 0000000000..27d97938da --- /dev/null +++ b/test/built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/fixed-offset-near-date-time-limits.js @@ -0,0 +1,54 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone.prototype.getpossibleinstantsfor +description: > + Call getPossibleInstantsFor with values near the date/time limit and a fixed offset. +features: [Temporal] +---*/ + +const oneHour = 1n * 60n * 60n * 1000n**3n; + +const minDt = new Temporal.PlainDateTime(-271821, 4, 19, 1, 0, 0, 0, 0, 0); +const minValidDt = new Temporal.PlainDateTime(-271821, 4, 20, 0, 0, 0, 0, 0, 0); +const maxDt = new Temporal.PlainDateTime(275760, 9, 13, 0, 0, 0, 0, 0, 0); + +let zero = new Temporal.TimeZone("+00"); +let plusOne = new Temporal.TimeZone("+01"); +let minusOne = new Temporal.TimeZone("-01"); + +// Try the minimum date-time. +assert.throws(RangeError, () => zero.getPossibleInstantsFor(minDt)); +assert.throws(RangeError, () => plusOne.getPossibleInstantsFor(minDt)); +assert.throws(RangeError, () => minusOne.getPossibleInstantsFor(minDt)); + +// Try the minimum valid date-time. +{ + let r = zero.getPossibleInstantsFor(minValidDt); + assert.sameValue(r.length, 1); + assert.sameValue(r[0].epochNanoseconds, -86_40000_00000_00000_00000n); +} + +{ + let r = minusOne.getPossibleInstantsFor(minValidDt); + assert.sameValue(r.length, 1); + assert.sameValue(r[0].epochNanoseconds, -86_40000_00000_00000_00000n + oneHour); +} + +assert.throws(RangeError, () => plusOne.getPossibleInstantsFor(minValidDt)); + +// Try the maximum valid date-time. +{ + let r = zero.getPossibleInstantsFor(maxDt); + assert.sameValue(r.length, 1); + assert.sameValue(r[0].epochNanoseconds, 86_40000_00000_00000_00000n); +} + +{ + let r = plusOne.getPossibleInstantsFor(maxDt); + assert.sameValue(r.length, 1); + assert.sameValue(r[0].epochNanoseconds, 86_40000_00000_00000_00000n - oneHour); +} + +assert.throws(RangeError, () => minusOne.getPossibleInstantsFor(maxDt)); diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-daylength-too-large.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-daylength-too-large.js new file mode 100644 index 0000000000..e715938123 --- /dev/null +++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-daylength-too-large.js @@ -0,0 +1,85 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: > + Round smallestUnit "day" with very large or very small divisor (dayLengthNs). +info: | + Temporal.ZonedDateTime.prototype.round ( roundTo ) + ... + 18. Let dayLengthNs be ℝ(endNs - startNs). + ... + 20. Let roundResult be ! RoundISODateTime(temporalDateTime.[[ISOYear]], + temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], temporalDateTime.[[ISOHour]], + temporalDateTime.[[ISOMinute]], temporalDateTime.[[ISOSecond]], + temporalDateTime.[[ISOMillisecond]], temporalDateTime.[[ISOMicrosecond]], + temporalDateTime.[[ISONanosecond]], roundingIncrement, smallestUnit, roundingMode, + dayLengthNs). + ... + + RoundISODateTime ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, + increment, unit, roundingMode [ , dayLength ] ) + ... + 4. Let roundedTime be ! RoundTime(hour, minute, second, millisecond, microsecond, nanosecond, + increment, unit, roundingMode, dayLength). + ... + + RoundTime ( hour, minute, second, millisecond, microsecond, nanosecond, increment, unit, + roundingMode [ , dayLengthNs ] ) + ... + 4. If unit is "day", then + ... + b. Let quantity be (((((hour × 60 + minute) × 60 + second) × 1000 + millisecond) × 1000 + + microsecond) × 1000 + nanosecond) / dayLengthNs. + ... +features: [Temporal] +---*/ + +class TimeZone extends Temporal.TimeZone { + #count = 0; + #nanoseconds; + + constructor(nanoseconds) { + super("UTC"); + this.#nanoseconds = nanoseconds; + } + getPossibleInstantsFor(dateTime) { + if (++this.#count === 2) { + return [new Temporal.Instant(this.#nanoseconds)]; + } + return super.getPossibleInstantsFor(dateTime); + } +} + +const maxInstant = 86_40000_00000_00000_00000n; +const minInstant = -86_40000_00000_00000_00000n; +const oneDay = 24n * 60n * 60n * 1000n * 1000n * 1000n + +// Divisor too large. +{ + let tz = new TimeZone(maxInstant); + let zoned = new Temporal.ZonedDateTime(0n, tz); + let result = zoned.round({ smallestUnit: "days" }); + assert(zoned.equals(result)); +} +{ + let tz = new TimeZone(maxInstant); + let zoned = new Temporal.ZonedDateTime(minInstant, tz); + let result = zoned.round({ smallestUnit: "days" }); + assert(zoned.equals(result)); +} + +// Divisor too small. +{ + let tz = new TimeZone(minInstant); + let zoned = new Temporal.ZonedDateTime(0n, tz); + let result = zoned.round({ smallestUnit: "days" }); + assert(zoned.equals(result)); +} +{ + let tz = new TimeZone(minInstant); + let zoned = new Temporal.ZonedDateTime(maxInstant - oneDay, tz); + let result = zoned.round({ smallestUnit: "days" }); + assert(zoned.equals(result)); +} diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-daylength-zero-or-negative.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-daylength-zero-or-negative.js new file mode 100644 index 0000000000..2103a0e6c3 --- /dev/null +++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-daylength-zero-or-negative.js @@ -0,0 +1,67 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: > + Round smallestUnit "day" with zero or negative day length. +info: | + Temporal.ZonedDateTime.prototype.round ( roundTo ) + ... + 18. Let dayLengthNs be ℝ(endNs - startNs). + 19. If dayLengthNs is 0, then + a. Throw a RangeError exception. + 20. Let roundResult be ! RoundISODateTime(temporalDateTime.[[ISOYear]], + temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], temporalDateTime.[[ISOHour]], + temporalDateTime.[[ISOMinute]], temporalDateTime.[[ISOSecond]], + temporalDateTime.[[ISOMillisecond]], temporalDateTime.[[ISOMicrosecond]], + temporalDateTime.[[ISONanosecond]], roundingIncrement, smallestUnit, roundingMode, + dayLengthNs). + ... + + RoundISODateTime ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, + increment, unit, roundingMode [ , dayLength ] ) + ... + 4. Let roundedTime be ! RoundTime(hour, minute, second, millisecond, microsecond, nanosecond, + increment, unit, roundingMode, dayLength). + ... + + RoundTime ( hour, minute, second, millisecond, microsecond, nanosecond, increment, unit, + roundingMode [ , dayLengthNs ] ) + ... + 4. If unit is "day", then + ... + b. Let quantity be (((((hour × 60 + minute) × 60 + second) × 1000 + millisecond) × 1000 + + microsecond) × 1000 + nanosecond) / dayLengthNs. + ... +features: [Temporal] +---*/ + +class TimeZone extends Temporal.TimeZone { + #count = 0; + #nanoseconds; + + constructor(nanoseconds) { + super("UTC"); + this.#nanoseconds = nanoseconds; + } + getPossibleInstantsFor(dateTime) { + if (++this.#count === 2) { + return [new Temporal.Instant(this.#nanoseconds)]; + } + return super.getPossibleInstantsFor(dateTime); + } +} + +{ + let tz = new TimeZone(0n); + let zoned = new Temporal.ZonedDateTime(0n, tz); + assert.throws(RangeError, () => zoned.round({ smallestUnit: "days" })); +} + +{ + let tz = new TimeZone(-1n); + let zoned = new Temporal.ZonedDateTime(0n, tz); + let result = zoned.round({ smallestUnit: "days" }); + assert(zoned.equals(result)); +} diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/minimum-instant-with-one-hour-offset.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/minimum-instant-with-one-hour-offset.js new file mode 100644 index 0000000000..8cdc361132 --- /dev/null +++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/minimum-instant-with-one-hour-offset.js @@ -0,0 +1,30 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: > + Throws a RangeError when ZonedDateTime at minimum instant and an explicit +1h offset. +info: | + Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options ] ) + ... + 21. Let epochNanoseconds be ? InterpretISODateTimeOffset(dateTimeResult.[[Year]], + dateTimeResult.[[Month]], dateTimeResult.[[Day]], dateTimeResult.[[Hour]], + dateTimeResult.[[Minute]], dateTimeResult.[[Second]], dateTimeResult.[[Millisecond]], + dateTimeResult.[[Microsecond]], dateTimeResult.[[Nanosecond]], option, offsetNanoseconds, + timeZone, disambiguation, offset, match exactly). + ... +features: [Temporal] +---*/ + +let zdt = new Temporal.ZonedDateTime(-86_40000_00000_00000_00000n, "UTC"); + +let temporalZonedDateTimeLike = { + offset: "+01", +}; + +let options = { + offset: "use", +}; + +assert.throws(RangeError, () => zdt.with(temporalZonedDateTimeLike, options)); diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time-with-offset.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time-with-offset.js new file mode 100644 index 0000000000..670462b899 --- /dev/null +++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time-with-offset.js @@ -0,0 +1,39 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: > + Throws a RangeError for the minimum date/value with UTC offset and an explicit offset. +info: | + Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options ] ) + ... + 21. Let epochNanoseconds be ? InterpretISODateTimeOffset(dateTimeResult.[[Year]], + dateTimeResult.[[Month]], dateTimeResult.[[Day]], dateTimeResult.[[Hour]], + dateTimeResult.[[Minute]], dateTimeResult.[[Second]], dateTimeResult.[[Millisecond]], + dateTimeResult.[[Microsecond]], dateTimeResult.[[Nanosecond]], option, offsetNanoseconds, + timeZone, disambiguation, offset, match exactly). + ... +features: [Temporal] +---*/ + +let zdt = new Temporal.ZonedDateTime(0n, "UTC"); + +let temporalZonedDateTimeLike = { + year: -271821, + month: 4, + day: 19, + hour: 1, + minute: 0, + second: 0, + millisecond: 0, + microsecond: 0, + nanosecond: 0, + offset: "+00", +}; + +let options = { + offset: "use", +}; + +assert.throws(RangeError, () => zdt.with(temporalZonedDateTimeLike, options)); diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time.js new file mode 100644 index 0000000000..8335407313 --- /dev/null +++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time.js @@ -0,0 +1,34 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: > + Throws a RangeError for the minimum date/value with UTC offset. +info: | + Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options ] ) + ... + 21. Let epochNanoseconds be ? InterpretISODateTimeOffset(dateTimeResult.[[Year]], + dateTimeResult.[[Month]], dateTimeResult.[[Day]], dateTimeResult.[[Hour]], + dateTimeResult.[[Minute]], dateTimeResult.[[Second]], dateTimeResult.[[Millisecond]], + dateTimeResult.[[Microsecond]], dateTimeResult.[[Nanosecond]], option, offsetNanoseconds, + timeZone, disambiguation, offset, match exactly). + ... +features: [Temporal] +---*/ + +let zdt = new Temporal.ZonedDateTime(0n, "UTC"); + +let temporalZonedDateTimeLike = { + year: -271821, + month: 4, + day: 19, + hour: 1, + minute: 0, + second: 0, + millisecond: 0, + microsecond: 0, + nanosecond: 0, +}; + +assert.throws(RangeError, () => zdt.with(temporalZonedDateTimeLike)); diff --git a/test/intl402/Temporal/TimeZone/prototype/getNextTransition/subtract-second-and-nanosecond-from-last-transition.js b/test/intl402/Temporal/TimeZone/prototype/getNextTransition/subtract-second-and-nanosecond-from-last-transition.js new file mode 100644 index 0000000000..1ace980f8b --- /dev/null +++ b/test/intl402/Temporal/TimeZone/prototype/getNextTransition/subtract-second-and-nanosecond-from-last-transition.js @@ -0,0 +1,55 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone.prototype.getnexttransition +description: > + Compute next transition when seconds resp. nanoseconds are subtracted from the last transition. +features: [Temporal] +---*/ + +// From : +// +// # Zone NAME STDOFF RULES FORMAT [UNTIL] +// Zone Europe/Paris 0:09:21 - LMT 1891 Mar 15 0:01 +// 0:09:21 - PMT 1911 Mar 11 0:01 # Paris MT + +let tz = new Temporal.TimeZone("Europe/Paris"); + +let zdt = new Temporal.PlainDateTime(1800, 1, 1).toZonedDateTime(tz); +assert.sameValue(zdt.toString(), "1800-01-01T00:00:00+00:09[Europe/Paris]"); +assert.sameValue(zdt.offsetNanoseconds, (9 * 60 + 21) * 1_000_000_000); + +// Ensure the first transition was correctly computed. +let first = tz.getNextTransition(zdt); +assert.sameValue(first.toString(), "1911-03-10T23:50:39Z"); +assert.sameValue(new Temporal.ZonedDateTime(first.epochNanoseconds, tz).toString(), + "1911-03-10T23:50:39+00:00[Europe/Paris]"); + +let next; + +// Compute the next transition starting from the first transition minus 1s. +let firstMinus1s = first.add({seconds: -1}); +assert.sameValue(firstMinus1s.toString(), "1911-03-10T23:50:38Z"); +assert.sameValue(new Temporal.ZonedDateTime(firstMinus1s.epochNanoseconds, tz).toString(), + "1911-03-10T23:59:59+00:09[Europe/Paris]"); +assert.sameValue(new Temporal.ZonedDateTime(firstMinus1s.epochNanoseconds, tz).offsetNanoseconds, + (9 * 60 + 21) * 1_000_000_000); + +next = tz.getNextTransition(firstMinus1s); +assert.sameValue(next.toString(), "1911-03-10T23:50:39Z"); +assert.sameValue(new Temporal.ZonedDateTime(next.epochNanoseconds, tz).toString(), + "1911-03-10T23:50:39+00:00[Europe/Paris]"); + +// Compute the next transition starting from the first transition minus 1ns. +let firstMinus1ns = first.add({nanoseconds: -1}); +assert.sameValue(firstMinus1ns.toString(), "1911-03-10T23:50:38.999999999Z"); +assert.sameValue(new Temporal.ZonedDateTime(firstMinus1ns.epochNanoseconds, tz).toString(), + "1911-03-10T23:59:59.999999999+00:09[Europe/Paris]"); +assert.sameValue(new Temporal.ZonedDateTime(firstMinus1ns.epochNanoseconds, tz).offsetNanoseconds, + (9 * 60 + 21) * 1_000_000_000); + +next = tz.getNextTransition(firstMinus1ns); +assert.sameValue(next.toString(), "1911-03-10T23:50:39Z"); +assert.sameValue(new Temporal.ZonedDateTime(next.epochNanoseconds, tz).toString(), + "1911-03-10T23:50:39+00:00[Europe/Paris]"); diff --git a/test/intl402/Temporal/TimeZone/prototype/getPreviousTransition/nanoseconds-subtracted-or-added-at-dst-transition.js b/test/intl402/Temporal/TimeZone/prototype/getPreviousTransition/nanoseconds-subtracted-or-added-at-dst-transition.js new file mode 100644 index 0000000000..fd5a860624 --- /dev/null +++ b/test/intl402/Temporal/TimeZone/prototype/getPreviousTransition/nanoseconds-subtracted-or-added-at-dst-transition.js @@ -0,0 +1,24 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone.prototype.getprevioustransition +description: > + Test previous transition when nanoseconds are subtracted resp. added to the DST transition. +features: [Temporal] +---*/ + +let tz = new Temporal.TimeZone("Europe/Berlin"); +let p = Temporal.Instant.from("2021-03-28T01:00:00Z"); + +assert.sameValue(tz.getPreviousTransition(p.add({nanoseconds: -1})).toString(), + "2020-10-25T01:00:00Z", + "DST transition minus one nanosecond"); + +assert.sameValue(tz.getPreviousTransition(p).toString(), + "2020-10-25T01:00:00Z", + "DST transition"); + +assert.sameValue(tz.getPreviousTransition(p.add({nanoseconds: +1})).toString(), + "2021-03-28T01:00:00Z", + "DST transition plus one nanosecond"); diff --git a/test/intl402/Temporal/TimeZone/supported-values-of.js b/test/intl402/Temporal/TimeZone/supported-values-of.js new file mode 100644 index 0000000000..bcc87b2f2d --- /dev/null +++ b/test/intl402/Temporal/TimeZone/supported-values-of.js @@ -0,0 +1,16 @@ +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone +description: > + TimeZone constructor accepts all time zone identifiers from Intl.supportedValuesOf. +features: [Temporal, Intl-enumeration] +---*/ + +// Ensure all identifiers are valid and canonical. +for (let id of Intl.supportedValuesOf("timeZone")) { + let tz = new Temporal.TimeZone(id); + + assert.sameValue(tz.id, id); +}