Tests for Duration rounding bug when rounding crosses a unit boundary

See https://github.com/tc39/proposal-temporal/issues/2563

The old behaviour was encoded in one test in staging, but the behaviour of
largestUnit in duration rounding has changed since that test was written.
Therefore I'm assuming that toString() should've been updated when that
happened.
This commit is contained in:
Philip Chimento 2023-05-02 11:12:51 -07:00 committed by Philip Chimento
parent 96727cae1c
commit 93304c7571
15 changed files with 331 additions and 7 deletions

View File

@ -0,0 +1,47 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.round
description: Rounding can cross unit boundaries up to the implicit largestUnit
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const relativeTo = new Temporal.PlainDate(2022, 1, 1);
const roundingMode = "expand";
// Positive, date units
{
const duration = new Temporal.Duration(1, 11, 0, 24);
const result = duration.round({ smallestUnit: "months", roundingMode, relativeTo });
TemporalHelpers.assertDuration(result, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "1 year 12 months balances to 2 years");
}
// Negative, date units
{
const duration = new Temporal.Duration(-1, -11, 0, -24);
const result = duration.round({ smallestUnit: "months", roundingMode, relativeTo });
TemporalHelpers.assertDuration(result, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "-1 year -12 months balances to -2 years");
}
// Positive, time units
{
const duration = new Temporal.Duration(0, 0, 0, 0, 1, 59, 59, 900);
const result = duration.round({ smallestUnit: "seconds", roundingMode });
TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, "1:59:60 balances to 2 hours");
}
// Negative, time units
{
const duration = new Temporal.Duration(0, 0, 0, 0, -1, -59, -59, -900);
const result = duration.round({ smallestUnit: "seconds", roundingMode });
TemporalHelpers.assertDuration(result, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, "-1:59:60 balances to -2 hours");
}
// No balancing if smallest unit is largest unit
{
const duration = new Temporal.Duration(0, 11, 0, 24);
const result = duration.round({ smallestUnit: "months", roundingMode, relativeTo });
TemporalHelpers.assertDuration(result, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, "12 months stays as is");
}

View File

@ -0,0 +1,40 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.tostring
description: Rounding can cross unit boundaries up to days
features: [Temporal]
---*/
const roundingMode = "expand";
// Positive, time units
{
const duration = new Temporal.Duration(0, 0, 0, 0, 1, 59, 59, 900);
assert.sameValue(duration.toString({ fractionalSecondDigits: 0, roundingMode }), "PT2H0S", "1:59:60 balances to 2 hours");
}
// Negative, time units
{
const duration = new Temporal.Duration(0, 0, 0, 0, -1, -59, -59, -900);
assert.sameValue(duration.toString({ fractionalSecondDigits: 0, roundingMode }), "-PT2H0S", "-1:59:60 balances to -2 hours");
}
// Positive, date and time units
{
const duration = new Temporal.Duration(1, 11, 0, 30, 23, 59, 59, 999, 999, 999);
assert.sameValue(duration.toString({ fractionalSecondDigits: 8, roundingMode }), "P1Y11M31DT0.00000000S", "units balance only up to days (positive)");
}
// Negative, date and time units
{
const duration = new Temporal.Duration(-1, -11, 0, -30, -23, -59, -59, -999, -999, -999);
assert.sameValue(duration.toString({ fractionalSecondDigits: 8, roundingMode }), "-P1Y11M31DT0.00000000S", "units balance only up to days (negative)");
}
// No balancing if smallest unit is largest unit
{
const duration = new Temporal.Duration(0, 0, 0, 0, 0, 0, 59, 900);
assert.sameValue(duration.toString({ fractionalSecondDigits: 0, roundingMode }), "PT60S", "60 seconds stays as is");
}

View File

@ -0,0 +1,14 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.instant.prototype.since
description: Rounding can cross unit boundaries up to largestUnit
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const earlier = new Temporal.Instant(0n);
const later = new Temporal.Instant(7199_000_000_000n);
const duration = earlier.since(later, { largestUnit: "hours", smallestUnit: "minutes", roundingMode: "expand" });
TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, "-1:59 balances to -2 hours");

View File

@ -0,0 +1,14 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.instant.prototype.until
description: Rounding can cross unit boundaries up to largestUnit
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const earlier = new Temporal.Instant(0n);
const later = new Temporal.Instant(7199_000_000_000n);
const duration = earlier.until(later, { largestUnit: "hours", smallestUnit: "minutes", roundingMode: "expand" });
TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, "1:59 balances to 2 hours");

View File

@ -0,0 +1,14 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.since
description: Rounding can cross unit boundaries up to largestUnit
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const earlier = new Temporal.PlainDate(2022, 1, 1);
const later = new Temporal.PlainDate(2023, 12, 25);
const duration = earlier.since(later, { largestUnit: "years", smallestUnit: "months", roundingMode: "expand" });
TemporalHelpers.assertDuration(duration, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "-1 year -11 months balances to -2 years");

View File

@ -0,0 +1,14 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.until
description: Rounding can cross unit boundaries up to largestUnit
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const earlier = new Temporal.PlainDate(2022, 1, 1);
const later = new Temporal.PlainDate(2023, 12, 25);
const duration = earlier.until(later, { largestUnit: "years", smallestUnit: "months", roundingMode: "expand" });
TemporalHelpers.assertDuration(duration, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "1 year 11 months balances to 2 years");

View File

@ -0,0 +1,33 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.since
description: Rounding can cross unit boundaries up to largestUnit
includes: [temporalHelpers.js]
features: [Temporal]
---*/
// Date units
{
const earlier = new Temporal.PlainDateTime(2022, 1, 1);
const later = new Temporal.PlainDateTime(2023, 12, 25);
const duration = earlier.since(later, { largestUnit: "years", smallestUnit: "months", roundingMode: "expand" });
TemporalHelpers.assertDuration(duration, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "-1 year -11 months balances to -2 years");
}
// Time units
{
const earlier = new Temporal.PlainDateTime(2000, 5, 2);
const later = new Temporal.PlainDateTime(2000, 5, 2, 1, 59, 59);
const duration = earlier.since(later, { largestUnit: "hours", smallestUnit: "minutes", roundingMode: "expand" });
TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, "-1:59 balances to -2 hours");
}
// Both
{
const earlier = new Temporal.PlainDateTime(1970, 1, 1);
const later = new Temporal.PlainDateTime(1971, 12, 31, 23, 59, 59, 999, 999, 999);
const duration = earlier.since(later, { largestUnit: "years", smallestUnit: "microseconds", roundingMode: "expand" });
TemporalHelpers.assertDuration(duration, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rounding down 1 ns balances to -2 years");
}

View File

@ -0,0 +1,33 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.until
description: Rounding can cross unit boundaries up to largestUnit
includes: [temporalHelpers.js]
features: [Temporal]
---*/
// Date units
{
const earlier = new Temporal.PlainDateTime(2022, 1, 1);
const later = new Temporal.PlainDateTime(2023, 12, 25);
const duration = earlier.until(later, { largestUnit: "years", smallestUnit: "months", roundingMode: "expand" });
TemporalHelpers.assertDuration(duration, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "1 year 11 months balances to 2 years");
}
// Time units
{
const earlier = new Temporal.PlainDateTime(2000, 5, 2);
const later = new Temporal.PlainDateTime(2000, 5, 2, 1, 59, 59);
const duration = earlier.until(later, { largestUnit: "hours", smallestUnit: "minutes", roundingMode: "expand" });
TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, "1:59 balances to 2 hours");
}
// Both
{
const earlier = new Temporal.PlainDateTime(1970, 1, 1);
const later = new Temporal.PlainDateTime(1971, 12, 31, 23, 59, 59, 999, 999, 999);
const duration = earlier.until(later, { largestUnit: "years", smallestUnit: "microseconds", roundingMode: "expand" });
TemporalHelpers.assertDuration(duration, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rounding up 1 ns balances to 2 years");
}

View File

@ -0,0 +1,14 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaintime.prototype.since
description: Rounding can cross unit boundaries up to largestUnit
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const earlier = new Temporal.PlainTime();
const later = new Temporal.PlainTime(1, 59, 59);
const duration = earlier.since(later, { largestUnit: "hours", smallestUnit: "minutes", roundingMode: "expand" });
TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, "-1:60 balances to -2 hours");

View File

@ -0,0 +1,14 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaintime.prototype.until
description: Rounding can cross unit boundaries up to largestUnit
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const earlier = new Temporal.PlainTime();
const later = new Temporal.PlainTime(1, 59, 59);
const duration = earlier.until(later, { largestUnit: "hours", smallestUnit: "minutes", roundingMode: "expand" });
TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, "1:60 balances to 2 hours");

View File

@ -0,0 +1,14 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plainyearmonth.prototype.since
description: Rounding can cross unit boundaries up to largestUnit
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const earlier = new Temporal.PlainYearMonth(2022, 1);
const later = new Temporal.PlainYearMonth(2023, 12);
const duration = earlier.since(later, { largestUnit: "years", smallestUnit: "months", roundingIncrement: 3, roundingMode: "expand" });
TemporalHelpers.assertDuration(duration, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "-1 year -12 months balances to -2 years");

View File

@ -0,0 +1,14 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plainyearmonth.prototype.until
description: Rounding can cross unit boundaries up to largestUnit
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const earlier = new Temporal.PlainYearMonth(2022, 1);
const later = new Temporal.PlainYearMonth(2023, 12);
const duration = earlier.until(later, { largestUnit: "years", smallestUnit: "months", roundingIncrement: 3, roundingMode: "expand" });
TemporalHelpers.assertDuration(duration, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "1 year 12 months balances to 2 years");

View File

@ -0,0 +1,33 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.since
description: Rounding can cross unit boundaries up to largestUnit
includes: [temporalHelpers.js]
features: [Temporal]
---*/
// Date units
{
const earlier = new Temporal.ZonedDateTime(1640995200_000_000_000n /* = 2022-01-01T00 */, "UTC");
const later = new Temporal.ZonedDateTime(1703462400_000_000_000n /* = 2023-12-25T00 */, "UTC");
const duration = earlier.since(later, { largestUnit: "years", smallestUnit: "months", roundingMode: "expand" });
TemporalHelpers.assertDuration(duration, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "-1 year -11 months balances to -2 years");
}
// Time units
{
const earlier = new Temporal.ZonedDateTime(0n, "UTC");
const later = new Temporal.ZonedDateTime(7199_000_000_000n, "UTC");
const duration = earlier.since(later, { largestUnit: "hours", smallestUnit: "minutes", roundingMode: "expand" });
TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, "-1:59 balances to -2 hours");
}
// Both
{
const earlier = new Temporal.ZonedDateTime(0n, "UTC");
const later = new Temporal.ZonedDateTime(63071999_999_999_999n /* = 1971-12-31T23:59:59.999999999 */, "UTC");
const duration = earlier.since(later, { largestUnit: "years", smallestUnit: "microseconds", roundingMode: "expand" });
TemporalHelpers.assertDuration(duration, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rounding down 1 ns balances to -2 years");
}

View File

@ -0,0 +1,33 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.until
description: Rounding can cross unit boundaries up to largestUnit
includes: [temporalHelpers.js]
features: [Temporal]
---*/
// Date units
{
const earlier = new Temporal.ZonedDateTime(1640995200_000_000_000n /* = 2022-01-01T00 */, "UTC");
const later = new Temporal.ZonedDateTime(1703462400_000_000_000n /* = 2023-12-25T00 */, "UTC");
const duration = earlier.until(later, { largestUnit: "years", smallestUnit: "months", roundingMode: "expand" });
TemporalHelpers.assertDuration(duration, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "1 year 11 months balances to 2 years");
}
// Time units
{
const earlier = new Temporal.ZonedDateTime(0n, "UTC");
const later = new Temporal.ZonedDateTime(7199_000_000_000n, "UTC");
const duration = earlier.until(later, { largestUnit: "hours", smallestUnit: "minutes", roundingMode: "expand" });
TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, "1:59 balances to 2 hours");
}
// Both
{
const earlier = new Temporal.ZonedDateTime(0n, "UTC");
const later = new Temporal.ZonedDateTime(63071999_999_999_999n /* = 1971-12-31T23:59:59.999999999 */, "UTC");
const duration = earlier.until(later, { largestUnit: "years", smallestUnit: "microseconds", roundingMode: "expand" });
TemporalHelpers.assertDuration(duration, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rounding up 1 ns balances to 2 years");
}

View File

@ -23,10 +23,3 @@ var d = Temporal.Duration.from({
microseconds: Number.MAX_SAFE_INTEGER
});
assert.sameValue(`${ d }`, "PT9016206453995.731991S");
// rounding can affect units up to seconds
var d4 = Temporal.Duration.from("P1Y1M1W1DT23H59M59.999999999S");
assert.sameValue(d4.toString({
fractionalSecondDigits: 8,
roundingMode: "halfExpand"
}), "P1Y1M1W1DT23H59M60.00000000S");