diff --git a/test/built-ins/Temporal/Duration/compare/precision-exact-mathematical-values-1.js b/test/built-ins/Temporal/Duration/compare/precision-exact-mathematical-values-1.js new file mode 100644 index 0000000000..f553c5df7d --- /dev/null +++ b/test/built-ins/Temporal/Duration/compare/precision-exact-mathematical-values-1.js @@ -0,0 +1,68 @@ +// 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.compare +description: > + Duration components are precise mathematical integers. +info: | + Temporal.Duration.compare ( one, two [ , options ] ) + + ... + 7. If any of one.[[Years]], two.[[Years]], one.[[Months]], two.[[Months]], one.[[Weeks]], or + two.[[Weeks]] are not 0, then + a. Let unbalanceResult1 be ? UnbalanceDurationRelative(one.[[Years]], one.[[Months]], + one.[[Weeks]], one.[[Days]], "day", relativeTo). + ... + 9. Let ns1 be ! TotalDurationNanoseconds(days1, one.[[Hours]], one.[[Minutes]], one.[[Seconds]], + one.[[Milliseconds]], one.[[Microseconds]], one.[[Nanoseconds]], shift1). + 10. Let ns2 be ! TotalDurationNanoseconds(days2, two.[[Hours]], two.[[Minutes]], two.[[Seconds]], + two.[[Milliseconds]], two.[[Microseconds]], two.[[Nanoseconds]], shift2). + 11. If ns1 > ns2, return 1𝔽. + 12. If ns1 < ns2, return -1𝔽. + 13. Return +0𝔽. + + UnbalanceDurationRelative ( years, months, weeks, days, largestUnit, relativeTo ) + + ... + 11. Else, + a. If any of years, months, and weeks are not zero, then + ... + iv. Repeat, while weeks ≠ 0, + 1. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneWeek). + 2. Set relativeTo to moveResult.[[RelativeTo]]. + 3. Set days to days + moveResult.[[Days]]. + 4. Set weeks to weeks - sign. + 12. Return ? CreateDateDurationRecord(years, months, weeks, days). +features: [Temporal] +---*/ + +var one = Temporal.Duration.from({ + days: Number.MAX_SAFE_INTEGER, + weeks: 3, +}); + +var two = Temporal.Duration.from({ + days: Number.MAX_SAFE_INTEGER + 3, + weeks: 0, +}); + +var cal = new class extends Temporal.Calendar { + dateAdd(date, duration, options) { + // Add one day when one week was requested. + if (duration.toString() === "P1W") { + return super.dateAdd(date, "P1D", options); + } + + // Only expect to add one week. + throw new Test262Error("dateAdd called with unexpected value"); + } +}("iso8601"); + +var pd = new Temporal.PlainDate(1970, 1, 1, cal); + +// |Number.MAX_SAFE_INTEGER + 1 + 1 + 1| is unequal to |Number.MAX_SAFE_INTEGER + 3| +// when the addition is performed using IEEE-754 semantics. But for compare we have +// to ensure exact mathematical computations are performed. + +assert.sameValue(Temporal.Duration.compare(one, two, {relativeTo: pd}), 0); diff --git a/test/built-ins/Temporal/Duration/compare/precision-exact-mathematical-values-2.js b/test/built-ins/Temporal/Duration/compare/precision-exact-mathematical-values-2.js new file mode 100644 index 0000000000..4c5f817144 --- /dev/null +++ b/test/built-ins/Temporal/Duration/compare/precision-exact-mathematical-values-2.js @@ -0,0 +1,68 @@ +// 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.compare +description: > + Duration components are precise mathematical integers. +info: | + Temporal.Duration.compare ( one, two [ , options ] ) + + ... + 7. If any of one.[[Years]], two.[[Years]], one.[[Months]], two.[[Months]], one.[[Weeks]], or + two.[[Weeks]] are not 0, then + a. Let unbalanceResult1 be ? UnbalanceDurationRelative(one.[[Years]], one.[[Months]], + one.[[Weeks]], one.[[Days]], "day", relativeTo). + ... + 9. Let ns1 be ! TotalDurationNanoseconds(days1, one.[[Hours]], one.[[Minutes]], one.[[Seconds]], + one.[[Milliseconds]], one.[[Microseconds]], one.[[Nanoseconds]], shift1). + 10. Let ns2 be ! TotalDurationNanoseconds(days2, two.[[Hours]], two.[[Minutes]], two.[[Seconds]], + two.[[Milliseconds]], two.[[Microseconds]], two.[[Nanoseconds]], shift2). + 11. If ns1 > ns2, return 1𝔽. + 12. If ns1 < ns2, return -1𝔽. + 13. Return +0𝔽. + + UnbalanceDurationRelative ( years, months, weeks, days, largestUnit, relativeTo ) + + ... + 11. Else, + a. If any of years, months, and weeks are not zero, then + ... + iv. Repeat, while weeks ≠ 0, + 1. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneWeek). + 2. Set relativeTo to moveResult.[[RelativeTo]]. + 3. Set days to days + moveResult.[[Days]]. + 4. Set weeks to weeks - sign. + 12. Return ? CreateDateDurationRecord(years, months, weeks, days). +features: [Temporal] +---*/ + +var one = Temporal.Duration.from({ + days: Number.MAX_SAFE_INTEGER, + weeks: 3, +}); + +var two = Temporal.Duration.from({ + days: Number.MAX_SAFE_INTEGER + 3, + weeks: 0, +}); + +var cal = new class extends Temporal.Calendar { + dateAdd(date, duration, options) { + // Add one day when one week was requested. + if (duration.toString() === "P1W") { + return super.dateAdd(date, "P1D", options); + } + + // Use zero duration to avoid a RangeError during CalculateOffsetShift. + return super.dateAdd(date, "PT0S", options); + } +}("iso8601"); + +var zdt = new Temporal.ZonedDateTime(0n, "UTC", cal); + +// |Number.MAX_SAFE_INTEGER + 1 + 1 + 1| is unequal to |Number.MAX_SAFE_INTEGER + 3| +// when the addition is performed using IEEE-754 semantics. But for compare we have +// to ensure exact mathematical computations are performed. + +assert.sameValue(Temporal.Duration.compare(one, two, {relativeTo: zdt}), 0); diff --git a/test/built-ins/Temporal/Duration/from/argument-string-fractional-precision.js b/test/built-ins/Temporal/Duration/from/argument-string-fractional-precision.js new file mode 100644 index 0000000000..418c063412 --- /dev/null +++ b/test/built-ins/Temporal/Duration/from/argument-string-fractional-precision.js @@ -0,0 +1,56 @@ +// 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.from +description: > + Fractional parts are computed using exact mathematical values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const tests = { + "PT0.999999999H": Temporal.Duration.from({ + minutes: 59, + seconds: 59, + milliseconds: 999, + microseconds: 996, + nanoseconds: 400, + }), + "PT0.000000011H": Temporal.Duration.from({ + minutes: 0, + seconds: 0, + milliseconds: 0, + microseconds: 39, + nanoseconds: 600, + }), + + "PT0.999999999M": Temporal.Duration.from({ + seconds: 59, + milliseconds: 999, + microseconds: 999, + nanoseconds: 940, + }), + "PT0.000000011M": Temporal.Duration.from({ + seconds: 0, + milliseconds: 0, + microseconds: 0, + nanoseconds: 660, + }), + + "PT0.999999999S": Temporal.Duration.from({ + milliseconds: 999, + microseconds: 999, + nanoseconds: 999, + }), + "PT0.000000011S": Temporal.Duration.from({ + milliseconds: 0, + microseconds: 0, + nanoseconds: 11, + }), +}; + +for (let [str, expected] of Object.entries(tests)) { + let actual = Temporal.Duration.from(str); + TemporalHelpers.assertDurationsEqual(actual, expected, str); +} diff --git a/test/built-ins/Temporal/Duration/from/argument-string-fractional-with-zero-subparts.js b/test/built-ins/Temporal/Duration/from/argument-string-fractional-with-zero-subparts.js new file mode 100644 index 0000000000..6e032de1d9 --- /dev/null +++ b/test/built-ins/Temporal/Duration/from/argument-string-fractional-with-zero-subparts.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.from +description: > + Throws when a fractional unit is present and a sub-part is zero. +features: [Temporal] +---*/ + +const invalid = [ + // Hours fraction with whole minutes. + "PT0.1H0M", + + // Hours fraction with fractional minutes. + "PT0.1H0.0M", + + // Hours fraction with whole seconds. + "PT0.1H0S", + + // Hours fraction with fractional seconds. + "PT0.1H0.0S", + + // Minutes fraction with whole seconds. + "PT0.1M0S", + + // Minutes fraction with fractional seconds. + "PT0.1M0.0S", +]; + +for (let string of invalid) { + assert.throws(RangeError, () => Temporal.Duration.from(string)); +} diff --git a/test/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-safe-integer.js b/test/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-safe-integer.js new file mode 100644 index 0000000000..c888faa6db --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-safe-integer.js @@ -0,0 +1,57 @@ +// 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: > + AddDuration computes on exact mathematical number values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const plainDate = new Temporal.PlainDate(1970, 1, 1); +const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601"); + +// Largest temporal unit is "day". +const duration1 = Temporal.Duration.from({nanoseconds: Number.MAX_SAFE_INTEGER}); +const duration2 = Temporal.Duration.from({nanoseconds: 2, days: 1}); +const nanos = BigInt(Number.MAX_SAFE_INTEGER) + 2n; + +TemporalHelpers.assertDuration( + duration1.add(duration2), + 0, 0, 0, + 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))), + Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n), + Number((nanos / (60n * 1_000_000_000n)) % 60n), + Number((nanos / 1_000_000_000n) % 60n), + Number((nanos / 1_000_000n) % 1000n), + Number((nanos / 1000n) % 1000n), + Number(nanos % 1000n), + "duration1.add(duration2)" +); + +TemporalHelpers.assertDuration( + duration1.add(duration2, {relativeTo: plainDate}), + 0, 0, 0, + 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))), + Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n), + Number((nanos / (60n * 1_000_000_000n)) % 60n), + Number((nanos / 1_000_000_000n) % 60n), + Number((nanos / 1_000_000n) % 1000n), + Number((nanos / 1000n) % 1000n), + Number(nanos % 1000n), + "duration1.add(duration2, {relativeTo: plainDate})" +); + +TemporalHelpers.assertDuration( + duration1.add(duration2, {relativeTo: zonedDateTime}), + 0, 0, 0, + 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))), + Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n), + Number((nanos / (60n * 1_000_000_000n)) % 60n), + Number((nanos / 1_000_000_000n) % 60n), + Number((nanos / 1_000_000n) % 1000n), + Number((nanos / 1000n) % 1000n), + Number(nanos % 1000n), + "duration1.add(duration2, {relativeTo: zonedDateTime})" +); diff --git a/test/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-value-2.js b/test/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-value-2.js new file mode 100644 index 0000000000..e7958b9bed --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-value-2.js @@ -0,0 +1,49 @@ +// 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 computes on exact mathematical number values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const plainDate = new Temporal.PlainDate(1970, 1, 1); +const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601"); + +// Largest temporal unit is "day". +const duration1 = Temporal.Duration.from({nanoseconds: Number.MAX_VALUE}); +const duration2 = Temporal.Duration.from({nanoseconds: Number.MAX_VALUE, days: 1}); +const nanos = BigInt(Number.MAX_VALUE) * 2n; + +TemporalHelpers.assertDuration( + duration1.add(duration2), + 0, 0, 0, + 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))), + Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n), + Number((nanos / (60n * 1_000_000_000n)) % 60n), + Number((nanos / 1_000_000_000n) % 60n), + Number((nanos / 1_000_000n) % 1000n), + Number((nanos / 1000n) % 1000n), + Number(nanos % 1000n), + "duration1.add(duration2)" +); + +TemporalHelpers.assertDuration( + duration1.add(duration2, {relativeTo: plainDate}), + 0, 0, 0, + 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))), + Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n), + Number((nanos / (60n * 1_000_000_000n)) % 60n), + Number((nanos / 1_000_000_000n) % 60n), + Number((nanos / 1_000_000n) % 1000n), + Number((nanos / 1000n) % 1000n), + Number(nanos % 1000n), + "duration1.add(duration2, {relativeTo: plainDate})" +); + +// Throws a RangeError because the intermediate instant is too large. +assert.throws(RangeError, () => { + duration1.add(duration2, {relativeTo: zonedDateTime}); +}); diff --git a/test/built-ins/Temporal/Duration/prototype/add/precision-exact-mathematical-values.js b/test/built-ins/Temporal/Duration/prototype/add/precision-exact-mathematical-values.js new file mode 100644 index 0000000000..9e19c9bc3d --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/add/precision-exact-mathematical-values.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.add +description: > + AddDuration computes on exact mathematical number values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// Largest temporal unit is "microsecond". +let duration1 = Temporal.Duration.from({microseconds: Number.MAX_SAFE_INTEGER + 1, nanoseconds: 0}); +let duration2 = Temporal.Duration.from({microseconds: 1, nanoseconds: 1000}); + +TemporalHelpers.assertDuration( + duration1.add(duration2), + 0, 0, 0, 0, + 0, 0, 0, 0, + 9007199254740994, + 0, + "duration1.add(duration2)" +); diff --git a/test/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-months.js b/test/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-months.js new file mode 100644 index 0000000000..0f73af6ca5 --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-months.js @@ -0,0 +1,52 @@ +// 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: > + BalanceDurationRelative computes on exact mathematical values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +{ + let date = new Temporal.PlainDate(1970, 1, 1); + + let duration = Temporal.Duration.from({ + months: Number.MAX_SAFE_INTEGER, + days: 31 + 28 + 31, + }); + + let result = duration.round({ + largestUnit: "months", + relativeTo: date, + }); + + TemporalHelpers.assertDuration( + result, + 0, 9007199254740994, 0, 0, + 0, 0, 0, + 0, 0, 0, + ); +} + +{ + let date = new Temporal.PlainDate(1970, 1, 1); + + let duration = Temporal.Duration.from({ + months: Number.MAX_SAFE_INTEGER, + days: 31 + 28 + 31 + 1, + }); + + let result = duration.round({ + largestUnit: "months", + relativeTo: date, + }); + + TemporalHelpers.assertDuration( + result, + 0, 9007199254740994, 0, 1, + 0, 0, 0, + 0, 0, 0, + ); +} diff --git a/test/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-weeks.js b/test/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-weeks.js new file mode 100644 index 0000000000..5d0bf427a3 --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-weeks.js @@ -0,0 +1,52 @@ +// 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: > + BalanceDurationRelative computes on exact mathematical values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +{ + let date = new Temporal.PlainDate(1970, 1, 1); + + let duration = Temporal.Duration.from({ + weeks: Number.MAX_SAFE_INTEGER, + days: 7 * 3, + }); + + let result = duration.round({ + largestUnit: "weeks", + relativeTo: date, + }); + + TemporalHelpers.assertDuration( + result, + 0, 0, 9007199254740994, 0, + 0, 0, 0, + 0, 0, 0, + ); +} + +{ + let date = new Temporal.PlainDate(1970, 1, 1); + + let duration = Temporal.Duration.from({ + weeks: Number.MAX_SAFE_INTEGER, + days: 7 * 3 + 1, + }); + + let result = duration.round({ + largestUnit: "weeks", + relativeTo: date, + }); + + TemporalHelpers.assertDuration( + result, + 0, 0, 9007199254740994, 1, + 0, 0, 0, + 0, 0, 0, + ); +} diff --git a/test/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-years-days.js b/test/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-years-days.js new file mode 100644 index 0000000000..cdc53562a1 --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-years-days.js @@ -0,0 +1,52 @@ +// 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: > + BalanceDurationRelative computes on exact mathematical values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +{ + let date = new Temporal.PlainDate(1970, 1, 1); + + let duration = Temporal.Duration.from({ + years: Number.MAX_SAFE_INTEGER, + days: 366 + 365 + 365, + }); + + let result = duration.round({ + largestUnit: "years", + relativeTo: date, + }); + + TemporalHelpers.assertDuration( + result, + 9007199254740994, 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + ); +} + +{ + let date = new Temporal.PlainDate(1970, 1, 1); + + let duration = Temporal.Duration.from({ + years: Number.MAX_SAFE_INTEGER, + days: 366 + 365 + 365 + 1, + }); + + let result = duration.round({ + largestUnit: "years", + relativeTo: date, + }); + + TemporalHelpers.assertDuration( + result, + 9007199254740994, 0, 0, 1, + 0, 0, 0, + 0, 0, 0, + ); +} diff --git a/test/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-years-months.js b/test/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-years-months.js new file mode 100644 index 0000000000..16dec1a5db --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-years-months.js @@ -0,0 +1,52 @@ +// 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: > + BalanceDurationRelative computes on exact mathematical values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +{ + let date = new Temporal.PlainDate(1970, 1, 1); + + let duration = Temporal.Duration.from({ + years: Number.MAX_SAFE_INTEGER, + months: 12 * 3, + }); + + let result = duration.round({ + largestUnit: "years", + relativeTo: date, + }); + + TemporalHelpers.assertDuration( + result, + 9007199254740994, 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + ); +} + +{ + let date = new Temporal.PlainDate(1970, 1, 1); + + let duration = Temporal.Duration.from({ + years: Number.MAX_SAFE_INTEGER, + months: 12 * 3 + 1, + }); + + let result = duration.round({ + largestUnit: "years", + relativeTo: date, + }); + + TemporalHelpers.assertDuration( + result, + 9007199254740994, 1, 0, 0, + 0, 0, 0, + 0, 0, 0, + ); +} diff --git a/test/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-years-with-calendar.js b/test/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-years-with-calendar.js new file mode 100644 index 0000000000..0afd469096 --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-years-with-calendar.js @@ -0,0 +1,53 @@ +// 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: > + BalanceDurationRelative computes on exact mathematical values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +let calendar = new class extends Temporal.Calendar { + #dateUntil = 0; + dateUntil(one, two, options) { + this.#dateUntil++; + + // Subtract one six times from 9007199254740996. + if (this.#dateUntil <= 6) { + return Temporal.Duration.from({months: 1}); + } + + // After subtracting six in total, months is 9007199254740996 - 6 = 9007199254740990. + // |MAX_SAFE_INTEGER| = 9007199254740991 is larger than 9007199254740990, so we exit + // from the loop. + if (this.#dateUntil === 7) { + return Temporal.Duration.from({months: Number.MAX_SAFE_INTEGER}); + } + + // Any additional calls to dateUntil are incorrect. + throw new Test262Error("dateUntil called more times than expected"); + } +}("iso8601"); + +let date = new Temporal.PlainDate(1970, 1, 1, calendar); + +let duration = Temporal.Duration.from({ + years: 0, + months: Number.MAX_SAFE_INTEGER + 4, // 9007199254740996 +}); + +let result = duration.round({ + largestUnit: "years", + relativeTo: date, +}); + +// Years is equal to the number of times we returned one month from dateUntil. +// Months is equal to 9007199254740996 - 6. +TemporalHelpers.assertDuration( + result, + 6, 9007199254740990, 0, 0, + 0, 0, 0, + 0, 0, 0, +); diff --git a/test/built-ins/Temporal/Duration/prototype/round/precision-exact-in-round-duration.js b/test/built-ins/Temporal/Duration/prototype/round/precision-exact-in-round-duration.js new file mode 100644 index 0000000000..c03ca57acc --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/round/precision-exact-in-round-duration.js @@ -0,0 +1,50 @@ +// 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 computes on exact mathematical values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +{ + let duration = Temporal.Duration.from({ + hours: 100_000, + nanoseconds: 5, + }); + + let rounded = duration.round({smallestUnit: "hours", roundingMode: "ceil"}); + + // If RoundDuration() was implemented with float64, precision loss would lead + // to computing an incorrect result. + // + // "PT100000H" with float64, but "PT100001H" with exact mathematical values. + TemporalHelpers.assertDuration( + rounded, + 0, 0, 0, 0, + 100001, 0, 0, + 0, 0, 0, + ); +} + +{ + let duration = Temporal.Duration.from({ + days: 1000, + nanoseconds: 5, + }); + + let rounded = duration.round({smallestUnit: "days", roundingMode: "ceil"}); + + // If RoundDuration() was implemented with float64, precision loss would lead + // to computing an incorrect result. + // + // "P1000D" with float64, but "P1001D" with exact mathematical values. + TemporalHelpers.assertDuration( + rounded, + 0, 0, 0, 1001, + 0, 0, 0, + 0, 0, 0, + ); +} diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-safe-integer.js b/test/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-safe-integer.js new file mode 100644 index 0000000000..34f8b85c71 --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-safe-integer.js @@ -0,0 +1,57 @@ +// 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: > + AddDuration computes on exact mathematical number values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const plainDate = new Temporal.PlainDate(1970, 1, 1); +const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601"); + +// Largest temporal unit is "day". +const duration1 = Temporal.Duration.from({nanoseconds: Number.MAX_SAFE_INTEGER}); +const duration2 = Temporal.Duration.from({nanoseconds: -2, days: -1}); +const nanos = BigInt(Number.MAX_SAFE_INTEGER) + 2n; + +TemporalHelpers.assertDuration( + duration1.subtract(duration2), + 0, 0, 0, + 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))), + Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n), + Number((nanos / (60n * 1_000_000_000n)) % 60n), + Number((nanos / 1_000_000_000n) % 60n), + Number((nanos / 1_000_000n) % 1000n), + Number((nanos / 1000n) % 1000n), + Number(nanos % 1000n), + "duration1.subtract(duration2)" +); + +TemporalHelpers.assertDuration( + duration1.subtract(duration2, {relativeTo: plainDate}), + 0, 0, 0, + 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))), + Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n), + Number((nanos / (60n * 1_000_000_000n)) % 60n), + Number((nanos / 1_000_000_000n) % 60n), + Number((nanos / 1_000_000n) % 1000n), + Number((nanos / 1000n) % 1000n), + Number(nanos % 1000n), + "duration1.subtract(duration2, {relativeTo: plainDate})" +); + +TemporalHelpers.assertDuration( + duration1.subtract(duration2, {relativeTo: zonedDateTime}), + 0, 0, 0, + 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))), + Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n), + Number((nanos / (60n * 1_000_000_000n)) % 60n), + Number((nanos / 1_000_000_000n) % 60n), + Number((nanos / 1_000_000n) % 1000n), + Number((nanos / 1000n) % 1000n), + Number(nanos % 1000n), + "duration1.subtract(duration2, {relativeTo: zonedDateTime})" +); diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-value-2.js b/test/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-value-2.js new file mode 100644 index 0000000000..ae2e909a25 --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-value-2.js @@ -0,0 +1,49 @@ +// 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 computes on exact mathematical number values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const plainDate = new Temporal.PlainDate(1970, 1, 1); +const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601"); + +// Largest temporal unit is "day". +const duration1 = Temporal.Duration.from({nanoseconds: Number.MAX_VALUE}); +const duration2 = Temporal.Duration.from({nanoseconds: -Number.MAX_VALUE, days: -1}); +const nanos = BigInt(Number.MAX_VALUE) * 2n; + +TemporalHelpers.assertDuration( + duration1.subtract(duration2), + 0, 0, 0, + 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))), + Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n), + Number((nanos / (60n * 1_000_000_000n)) % 60n), + Number((nanos / 1_000_000_000n) % 60n), + Number((nanos / 1_000_000n) % 1000n), + Number((nanos / 1000n) % 1000n), + Number(nanos % 1000n), + "duration1.subtract(duration2)" +); + +TemporalHelpers.assertDuration( + duration1.subtract(duration2, {relativeTo: plainDate}), + 0, 0, 0, + 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))), + Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n), + Number((nanos / (60n * 1_000_000_000n)) % 60n), + Number((nanos / 1_000_000_000n) % 60n), + Number((nanos / 1_000_000n) % 1000n), + Number((nanos / 1000n) % 1000n), + Number(nanos % 1000n), + "duration1.subtract(duration2, {relativeTo: plainDate})" +); + +// Throws a RangeError because the intermediate instant is too large. +assert.throws(RangeError, () => { + duration1.subtract(duration2, {relativeTo: zonedDateTime}); +}); diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/precision-exact-mathematical-values.js b/test/built-ins/Temporal/Duration/prototype/subtract/precision-exact-mathematical-values.js new file mode 100644 index 0000000000..0f88a3ec8f --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/subtract/precision-exact-mathematical-values.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.subtract +description: > + AddDuration computes on exact mathematical number values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// Largest temporal unit is "microsecond". +let duration1 = Temporal.Duration.from({microseconds: Number.MAX_SAFE_INTEGER + 1, nanoseconds: 0}); +let duration2 = Temporal.Duration.from({microseconds: -1, nanoseconds: -1000}); + +TemporalHelpers.assertDuration( + duration1.subtract(duration2), + 0, 0, 0, 0, + 0, 0, 0, 0, + 9007199254740994, + 0, + "duration1.subtract(duration2)" +); diff --git a/test/built-ins/Temporal/Duration/prototype/toString/precision-exact-mathematical-values.js b/test/built-ins/Temporal/Duration/prototype/toString/precision-exact-mathematical-values.js new file mode 100644 index 0000000000..bf7bc7e1e5 --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/toString/precision-exact-mathematical-values.js @@ -0,0 +1,36 @@ +// 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 precise mathematical integers. +info: | + TemporalDurationToString ( years, months, weeks, days, hours, minutes, seconds, milliseconds, + microseconds, nanoseconds, precision ) + ... + 2. Set microseconds to microseconds + RoundTowardsZero(nanoseconds / 1000). + 3. Set nanoseconds to remainder(nanoseconds, 1000). + 4. Set milliseconds to milliseconds + RoundTowardsZero(microseconds / 1000). + 5. Set microseconds to remainder(microseconds, 1000). + 6. Set seconds to seconds + RoundTowardsZero(milliseconds / 1000). + 7. Set milliseconds to remainder(milliseconds, 1000). + ... +features: [Temporal] +---*/ + +{ + let d = Temporal.Duration.from({microseconds: 10000000000000004000, nanoseconds: 1000}); + + // "PT10000000000000.004096S" with float64. + // "PT10000000000000.004097S" with exact precision. + assert.sameValue(d.toString(), "PT10000000000000.004097S"); +} + +{ + let d = Temporal.Duration.from({seconds: 10000000000000004000, microseconds: 1000}); + + // "PT10000000000000004000.001S" with float64. + // "PT10000000000000004096.001S" with exact precision. + assert.sameValue(d.toString(), "PT10000000000000004096.001S"); +} diff --git a/test/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-1.js b/test/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-1.js new file mode 100644 index 0000000000..b56bb1d719 --- /dev/null +++ b/test/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-1.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.plaintime.prototype.add +description: > + Duration components are precise mathematical integers. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +let duration = Temporal.Duration.from({ + microseconds: Number.MAX_SAFE_INTEGER, + nanoseconds: 1000, +}); + +let time = Temporal.PlainTime.from({ + microsecond: 1, +}); + +let result = time.add(duration); + +TemporalHelpers.assertPlainTime(result, 23, 47, 34, 740, 993, 0); diff --git a/test/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-2.js b/test/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-2.js new file mode 100644 index 0000000000..d86e38e8f6 --- /dev/null +++ b/test/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-2.js @@ -0,0 +1,21 @@ +// 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: > + Duration components are precise mathematical integers. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +let duration = Temporal.Duration.from({ + hours: Number.MAX_VALUE, + minutes: Number.MAX_VALUE, +}); + +let time = new Temporal.PlainTime(0, 0, 0, 0, 0, 0); + +let result = time.add(duration); + +TemporalHelpers.assertPlainTime(result, 10, 8, 0, 0, 0, 0); diff --git a/test/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-3.js b/test/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-3.js new file mode 100644 index 0000000000..79161ac477 --- /dev/null +++ b/test/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-3.js @@ -0,0 +1,25 @@ +// 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: > + Duration components are precise mathematical integers. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +let duration = Temporal.Duration.from({ + hours: Number.MAX_VALUE, + minutes: Number.MAX_VALUE, + seconds: Number.MAX_VALUE, + milliseconds: Number.MAX_VALUE, + microseconds: Number.MAX_VALUE, + nanoseconds: Number.MAX_VALUE, +}); + +let time = new Temporal.PlainTime(0, 0, 0, 0, 0, 0); + +let result = time.add(duration); + +TemporalHelpers.assertPlainTime(result, 3, 53, 35, 351, 226, 368); diff --git a/test/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-1.js b/test/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-1.js new file mode 100644 index 0000000000..a88c00ae2a --- /dev/null +++ b/test/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-1.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.plaintime.prototype.subtract +description: > + Duration components are precise mathematical integers. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +let duration = Temporal.Duration.from({ + microseconds: Number.MIN_SAFE_INTEGER, + nanoseconds: -1000, +}); + +let time = Temporal.PlainTime.from({ + microsecond: 1, +}); + +let result = time.subtract(duration); + +TemporalHelpers.assertPlainTime(result, 23, 47, 34, 740, 993, 0); diff --git a/test/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-2.js b/test/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-2.js new file mode 100644 index 0000000000..0a5ad0d5d2 --- /dev/null +++ b/test/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-2.js @@ -0,0 +1,21 @@ +// 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: > + Duration components are precise mathematical integers. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +let duration = Temporal.Duration.from({ + hours: -Number.MAX_VALUE, + minutes: -Number.MAX_VALUE, +}); + +let time = new Temporal.PlainTime(0, 0, 0, 0, 0, 0); + +let result = time.subtract(duration); + +TemporalHelpers.assertPlainTime(result, 10, 8, 0, 0, 0, 0); diff --git a/test/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-3.js b/test/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-3.js new file mode 100644 index 0000000000..cda03c1da0 --- /dev/null +++ b/test/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-3.js @@ -0,0 +1,25 @@ +// 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: > + Duration components are precise mathematical integers. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +let duration = Temporal.Duration.from({ + hours: -Number.MAX_VALUE, + minutes: -Number.MAX_VALUE, + seconds: -Number.MAX_VALUE, + milliseconds: -Number.MAX_VALUE, + microseconds: -Number.MAX_VALUE, + nanoseconds: -Number.MAX_VALUE, +}); + +let time = new Temporal.PlainTime(0, 0, 0, 0, 0, 0); + +let result = time.subtract(duration); + +TemporalHelpers.assertPlainTime(result, 3, 53, 35, 351, 226, 368); diff --git a/test/intl402/Temporal/TimeZone/non-canonical-utc.js b/test/intl402/Temporal/TimeZone/non-canonical-utc.js new file mode 100644 index 0000000000..f5b125e52f --- /dev/null +++ b/test/intl402/Temporal/TimeZone/non-canonical-utc.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.timezone +description: > + TimeZone constructor canonicalises its input. +features: [Temporal] +---*/ + +const testCases = { + "Etc/GMT": "UTC", + "Etc/GMT+0": "UTC", + "Etc/GMT-0": "UTC", + "Etc/GMT0": "UTC", + "Etc/Greenwich": "UTC", + "Etc/UCT": "UTC", + "Etc/UTC": "UTC", + "Etc/Universal": "UTC", + "Etc/Zulu": "UTC", +}; + +for (let [id, canonical] of Object.entries(testCases)) { + let tz = new Temporal.TimeZone(id); + + assert.sameValue(tz.id, canonical); +}