Skip to content

Commit

Permalink
Temporal: Tests for corner case in TZDB involving DST transition in 1919
Browse files Browse the repository at this point in the history
These tests add coverage for a corner case in the TZDB. In spring 1919,
the America/Toronto time zone switched to DST at 23:30 on March 30th,
skipping an hour ahead to 00:30 on March 31st. This meant that both March
30th and March 31st were 23.5-hour days.

See: tc39/proposal-temporal#2910
  • Loading branch information
ptomato committed Jul 20, 2024
1 parent 5e60255 commit 87add44
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.tozoneddatetime
description: Test TZDB edge case where start of day is not 00:00 nor 01:00
includes: [temporalHelpers.js]
features: [Temporal]
---*/

// DST spring-forward hour skipped from 1919-03-30T23:30 to 1919-03-31T00:30, so
// day starts at 00:30
const instance = new Temporal.PlainDate(1919, 3, 31);
const startOfDay = instance.toZonedDateTime("America/Toronto");
const midnightDisambiguated = instance.toZonedDateTime({
timeZone: "America/Toronto",
plainTime: new Temporal.PlainTime(),
});
TemporalHelpers.assertDuration(
startOfDay.until(midnightDisambiguated),
0, 0, 0, 0, 0, /* minutes = */ 30, 0, 0, 0, 0,
"start of day is 30 minutes earlier than following the disambiguation strategy for midnight"
);

assert.sameValue(
startOfDay.epochNanoseconds,
instance.toZonedDateTime({ timeZone: "America/Toronto" }).epochNanoseconds,
"omitted plainTime is the same result as using the string shorthand"
);
assert.sameValue(
startOfDay.epochNanoseconds,
instance.toZonedDateTime({ timeZone: "America/Toronto", plainTime: undefined }).epochNanoseconds,
"explicitly undefined plainTime is the same result as using the string shorthand"
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.from
description: Test TZDB edge case where start of day is not 00:00 nor 01:00
includes: [temporalHelpers.js]
features: [Temporal]
---*/

// DST spring-forward hour skipped from 1919-03-30T23:30 to 1919-03-31T00:30, so
// day starts at 00:30
const startOfDay = Temporal.ZonedDateTime.from("1919-03-31[America/Toronto]");
const midnightDisambiguated = Temporal.ZonedDateTime.from("1919-03-31T00[America/Toronto]");
TemporalHelpers.assertDuration(
startOfDay.until(midnightDisambiguated),
0, 0, 0, 0, 0, /* minutes = */ 30, 0, 0, 0, 0,
"start of day is 30 minutes earlier than following the disambiguation strategy for midnight"
);

assert.sameValue(
midnightDisambiguated.epochNanoseconds,
Temporal.ZonedDateTime.from({ year: 1919, month: 3, day: 31, timeZone: "America/Toronto" }).epochNanoseconds,
"start of day magic doesn't happen with property bag, missing properties are zero"
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-get-temporal.zoneddatetime.prototype.hoursinday
description: Test TZDB edge case where start of day is not 00:00 nor 01:00
features: [Temporal]
---*/

// DST spring-forward hour skipped at 1919-03-30T23:30 (23.5 hour day)
const dayBefore = Temporal.ZonedDateTime.from({
year: 1919,
month: 3,
day: 30,
hour: 12,
timeZone: "America/Toronto",
});
assert.sameValue(dayBefore.hoursInDay, 23.5, "1919-03-30 had 23.5 hours in America/Toronto");

// Following day was also 23.5 hours
const dayAfter = Temporal.ZonedDateTime.from({
year: 1919,
month: 3,
day: 31,
hour: 12,
timeZone: "America/Toronto",
});
assert.sameValue(dayAfter.hoursInDay, 23.5, "1919-03-31 had 23.5 hours in America/Toronto");
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-get-temporal.zoneddatetime.prototype.round
description: Test TZDB edge case where start of day is not 00:00 nor 01:00
includes: [temporalHelpers.js]
features: [Temporal]
---*/

// DST spring-forward hour skipped at 1919-03-30T23:30 (23.5 hour day)
// 11.75 hours is 0.5
const dayBefore = Temporal.ZonedDateTime.from({
year: 1919,
month: 3,
day: 30,
hour: 11,
minute: 45,
timeZone: "America/Toronto",
});
TemporalHelpers.assertPlainDateTime(
dayBefore.round({ smallestUnit: "day" }).toPlainDateTime(),
1919, 3, "M03", 31, 0, 30, 0, 0, 0, 0,
"1919-03-30T11:45 rounds up to start of next day with halfExpand"
);
TemporalHelpers.assertPlainDateTime(
dayBefore.round({ smallestUnit: "day", roundingMode: "halfTrunc" }).toPlainDateTime(),
1919, 3, "M03", 30, 0, 0, 0, 0, 0, 0,
"1919-03-30T11:45 rounds down to start of this day with halfTrunc"
);

// Following day was also 23.5 hours
const dayAfter = Temporal.ZonedDateTime.from({
year: 1919,
month: 3,
day: 31,
hour: 12,
minute: 15,
timeZone: "America/Toronto",
});
TemporalHelpers.assertPlainDateTime(
dayAfter.round({ smallestUnit: "day" }).toPlainDateTime(),
1919, 4, "M04", 1, 0, 0, 0, 0, 0, 0,
"1919-03-31T12:15 rounds up to start of next day with halfExpand"
);
TemporalHelpers.assertPlainDateTime(
dayAfter.round({ smallestUnit: "day", roundingMode: "halfTrunc" }).toPlainDateTime(),
1919, 3, "M03", 31, 0, 30, 0, 0, 0, 0,
"1919-03-31T12:15 rounds down to start of this day with halfTrunc"
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.startofday
description: Test TZDB edge case where start of day is not 00:00 nor 01:00
features: [Temporal]
---*/

// DST spring-forward hour skipped at 1919-03-30T23:30, so the following day
// started at 00:30
const instance = Temporal.ZonedDateTime.from({
year: 1919,
month: 3,
day: 31,
hour: 12,
timeZone: "America/Toronto",
});
const result = instance.startOfDay();
assert.sameValue(result.hour, 0, "1919-03-31 started at hour 0");
assert.sameValue(result.minute, 30, "1919-03-31 started at minute 30");
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.withplaintime
description: Test TZDB edge case where start of day is not 00:00 nor 01:00
includes: [temporalHelpers.js]
features: [Temporal]
---*/

// DST spring-forward hour skipped from 1919-03-30T23:30 to 1919-03-31T00:30, so
// day starts at 00:30
const instance = Temporal.ZonedDateTime.from({
year: 1919,
month: 3,
day: 31,
hour: 12,
timeZone: "America/Toronto",
});
const startOfDay = instance.withPlainTime();
const midnightDisambiguated = instance.withPlainTime(new Temporal.PlainTime());
TemporalHelpers.assertDuration(
startOfDay.until(midnightDisambiguated),
0, 0, 0, 0, 0, /* minutes = */ 30, 0, 0, 0, 0,
"start of day is 30 minutes earlier than following the disambiguation strategy for midnight"
);

0 comments on commit 87add44

Please sign in to comment.