Skip to content

Commit 561935c

Browse files
committed
[Temporal] Formatting Plain* Temporal objects should ignore the timezones.
These are tests for proposal-temporal's normative change PR tc39/proposal-temporal#3246 and polyfill fix PR tc39/proposal-temporal#3257
1 parent c9aa612 commit 561935c

File tree

8 files changed

+373
-0
lines changed

8 files changed

+373
-0
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (C) 2026 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
/*---
5+
esid: sec-datetime-format-functions
6+
description: Intl.DateTimeFormat.prototype.format ignores timezone when isPlain is true.
7+
features: [Temporal]
8+
---*/
9+
10+
// Non existent date in the Pacific/Apia timezone.
11+
const datetime_apia = '2011-12-30T12:00:00';
12+
// Non existent time in the America/Los_Angeles timezone.
13+
const datetime_los_angeles = '2026-03-08T02:00:00';
14+
15+
const pdt_apia = Temporal.PlainDateTime.from(datetime_apia);
16+
const pdt_los_angeles = Temporal.PlainDateTime.from(datetime_los_angeles);
17+
18+
const pd_apia = Temporal.PlainDate.from(datetime_apia);
19+
const pd_los_angeles = Temporal.PlainDate.from(datetime_los_angeles);
20+
21+
const pt_apia = Temporal.PlainTime.from(datetime_apia);
22+
const pt_los_angeles = Temporal.PlainTime.from(datetime_los_angeles);
23+
24+
const dtf_apia = new Intl.DateTimeFormat('en-US', { dateStyle: 'short', timeStyle: 'short', timeZone: 'Pacific/Apia' });
25+
const dtf_los_angeles = new Intl.DateTimeFormat('en-US', { dateStyle: 'short', timeStyle: 'short', timeZone: 'America/Los_Angeles' });
26+
27+
// PlainDateTime
28+
assert.sameValue(
29+
dtf_apia.format(pdt_apia),
30+
"12/30/11, 12:00 PM",
31+
"day is calculated correctly, ignoring the Pacific/Apia timezone"
32+
);
33+
34+
assert.sameValue(
35+
dtf_los_angeles.format(pdt_los_angeles),
36+
"3/8/26, 2:00 AM",
37+
"hour is calculated correctly with the America/Los_Angeles timezone"
38+
);
39+
40+
// PlainDate
41+
assert.sameValue(
42+
dtf_apia.format(pd_apia),
43+
"12/30/11",
44+
"day is calculated correctly, ignoring the Pacific/Apia timezone"
45+
);
46+
47+
// PlainTime
48+
assert.sameValue(
49+
dtf_los_angeles.format(pt_los_angeles),
50+
"2:00 AM",
51+
"hour is calculated correctly with the America/Los_Angeles timezone"
52+
);
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright (C) 2026 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
/*---
5+
esid: sec-datetime-format-functions
6+
description: Intl.DateTimeFormat.prototype.formatRange ignores timezone when isPlain is true.
7+
features: [Temporal]
8+
---*/
9+
10+
// Non existent date in the Pacific/Apia timezone.
11+
const datetime_apia = '2011-12-30T12:00:00';
12+
// Non existent time in the America/Los_Angeles timezone.
13+
const datetime_los_angeles = '2026-03-08T02:00:00';
14+
15+
const pdt_apia = Temporal.PlainDateTime.from(datetime_apia);
16+
const pdt_los_angeles = Temporal.PlainDateTime.from(datetime_los_angeles);
17+
18+
const pd_apia = Temporal.PlainDate.from(datetime_apia);
19+
const pd_los_angeles = Temporal.PlainDate.from(datetime_los_angeles);
20+
21+
const pt_apia = Temporal.PlainTime.from(datetime_apia);
22+
const pt_los_angeles = Temporal.PlainTime.from(datetime_los_angeles);
23+
24+
const dtf_apia = new Intl.DateTimeFormat('en-US', { dateStyle: 'short', timeStyle: 'short', timeZone: 'Pacific/Apia' });
25+
const dtf_los_angeles = new Intl.DateTimeFormat('en-US', { dateStyle: 'short', timeStyle: 'short', timeZone: 'America/Los_Angeles' });
26+
27+
const usDateRangeSeparator = new Intl.DateTimeFormat("en-US", { dateStyle: "short" })
28+
.formatRangeToParts(1 * 86400 * 1000, 366 * 86400 * 1000)
29+
.find((part) => part.type === "literal" && part.source === "shared").value;
30+
31+
// PlainDateTime
32+
assert.sameValue(
33+
dtf_apia.formatRange(pdt_apia, pdt_los_angeles),
34+
`12/30/11, 12:00 PM${usDateRangeSeparator}3/8/26, 2:00 AM`,
35+
"day is calculated correctly, ignoring the Pacific/Apia timezone"
36+
);
37+
38+
assert.sameValue(
39+
dtf_los_angeles.formatRange(pdt_apia, pdt_los_angeles),
40+
`12/30/11, 12:00 PM${usDateRangeSeparator}3/8/26, 2:00 AM`,
41+
"hour is calculated correctly with the America/Los_Angeles timezone"
42+
);
43+
44+
// PlainDate
45+
assert.sameValue(
46+
dtf_apia.formatRange(pd_apia, pd_los_angeles),
47+
`12/30/11${usDateRangeSeparator}3/8/26`,
48+
"day is calculated correctly, ignoring the Pacific/Apia timezone"
49+
);
50+
51+
// PlainTime
52+
assert.sameValue(
53+
dtf_los_angeles.formatRange(pt_apia, pt_los_angeles),
54+
`12:00 PM${usDateRangeSeparator}2:00 AM`,
55+
"hour is calculated correctly with the America/Los_Angeles timezone"
56+
);
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (C) 2026 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
/*---
5+
esid: sec-datetime-format-functions
6+
description: Intl.DateTimeFormat.prototype.formatRangeToParts ignores timezone when isPlain is true.
7+
features: [Temporal]
8+
---*/
9+
10+
// Non existent date in the Pacific/Apia timezone.
11+
const datetime_apia = '2011-12-30T12:00:00';
12+
// Non existent time in the America/Los_Angeles timezone.
13+
const datetime_los_angeles = '2026-03-08T02:00:00';
14+
15+
const pdt_apia = Temporal.PlainDateTime.from(datetime_apia);
16+
const pdt_los_angeles = Temporal.PlainDateTime.from(datetime_los_angeles);
17+
18+
const pd_apia = Temporal.PlainDate.from(datetime_apia);
19+
const pd_los_angeles = Temporal.PlainDate.from(datetime_los_angeles);
20+
21+
const pt_apia = Temporal.PlainTime.from(datetime_apia);
22+
const pt_los_angeles = Temporal.PlainTime.from(datetime_los_angeles);
23+
24+
const dtf_apia = new Intl.DateTimeFormat('en-US', { dateStyle: 'short', timeStyle: 'short', timeZone: 'Pacific/Apia' });
25+
const dtf_los_angeles = new Intl.DateTimeFormat('en-US', { dateStyle: 'short', timeStyle: 'short', timeZone: 'America/Los_Angeles' });
26+
27+
// PlainDateTime
28+
assert.sameValue(
29+
dtf_apia.formatRangeToParts(pdt_apia, pdt_los_angeles)[2].value,
30+
"30",
31+
"day is calculated correctly, ignoring the Pacific/Apia timezone"
32+
);
33+
34+
assert.sameValue(
35+
dtf_los_angeles.formatRangeToParts(pdt_apia, pdt_los_angeles)[18].value,
36+
"2",
37+
"hour is calculated correctly with the America/Los_Angeles timezone"
38+
);
39+
40+
// PlainDate
41+
assert.sameValue(
42+
dtf_apia.formatRangeToParts(pd_apia, pd_los_angeles)[2].value,
43+
"30",
44+
"day is calculated correctly, ignoring the Pacific/Apia timezone"
45+
);
46+
47+
// PlainTime
48+
assert.sameValue(
49+
dtf_los_angeles.formatRangeToParts(pt_apia, pt_los_angeles)[6].value,
50+
"2",
51+
"hour is calculated correctly with the America/Los_Angeles timezone"
52+
);
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (C) 2026 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
/*---
5+
esid: sec-datetime-format-functions
6+
description: Intl.DateTimeFormat.prototype.formatToParts ignores timezone when isPlain is true.
7+
features: [Temporal]
8+
---*/
9+
10+
// Non existent date in the Pacific/Apia timezone.
11+
const datetime_apia = '2011-12-30T12:00:00';
12+
// Non existent time in the America/Los_Angeles timezone.
13+
const datetime_los_angeles = '2026-03-08T02:00:00';
14+
15+
const pdt_apia = Temporal.PlainDateTime.from(datetime_apia);
16+
const pdt_los_angeles = Temporal.PlainDateTime.from(datetime_los_angeles);
17+
18+
const pd_apia = Temporal.PlainDate.from(datetime_apia);
19+
const pd_los_angeles = Temporal.PlainDate.from(datetime_los_angeles);
20+
21+
const pt_apia = Temporal.PlainTime.from(datetime_apia);
22+
const pt_los_angeles = Temporal.PlainTime.from(datetime_los_angeles);
23+
24+
const dtf_apia = new Intl.DateTimeFormat('en-US', { dateStyle: 'short', timeStyle: 'short', timeZone: 'Pacific/Apia' });
25+
const dtf_los_angeles = new Intl.DateTimeFormat('en-US', { dateStyle: 'short', timeStyle: 'short', timeZone: 'America/Los_Angeles' });
26+
27+
// PlainDateTime
28+
assert.sameValue(
29+
dtf_apia.formatToParts(pdt_apia)[2].value,
30+
"30",
31+
"day is calculated correctly, ignoring the Pacific/Apia timezone"
32+
);
33+
34+
assert.sameValue(
35+
dtf_los_angeles.formatToParts(pdt_los_angeles)[6].value,
36+
"2",
37+
"hour is calculated correctly with the America/Los_Angeles timezone"
38+
);
39+
40+
// PlainDate
41+
assert.sameValue(
42+
dtf_apia.formatToParts(pd_apia)[2].value,
43+
"30",
44+
"day is calculated correctly, ignoring the Pacific/Apia timezone"
45+
);
46+
47+
// PlainTime
48+
assert.sameValue(
49+
dtf_los_angeles.formatToParts(pt_los_angeles)[0].value,
50+
"2",
51+
"hour is calculated correctly with the America/Los_Angeles timezone"
52+
);
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright (C) 2026 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
/*---
5+
esid: sec-temporal.instant.prototype.tolocalestring
6+
description: Formatting a Plain* object should not break the formatter for Instant.
7+
features: [Temporal]
8+
---*/
9+
10+
const datestring = '2026-03-29T02:30:15+01:00';
11+
const instant = Temporal.Instant.from(datestring);
12+
const pdt = Temporal.PlainDateTime.from(datestring);
13+
14+
assert.sameValue(
15+
pdt.toLocaleString('en', { timeStyle: 'long' }),
16+
'2:30:15 AM'
17+
);
18+
19+
assert.sameValue(
20+
instant.toLocaleString('en', { timeStyle: 'long', timeZone: 'America/Los_Angeles' }),
21+
'6:30:15 PM PDT'
22+
);
23+
24+
assert.sameValue(
25+
instant.toLocaleString('en', { timeStyle: 'long', timeZone: 'Europe/Berlin' }),
26+
'3:30:15 AM GMT+2'
27+
);
28+
29+
assert.sameValue(
30+
new Temporal.PlainDate(2011, 12, 30).toLocaleString('en'),
31+
'12/30/2011'
32+
),
33+
34+
assert.sameValue(
35+
new Temporal.Instant(0n).toLocaleString("en", { era: "narrow" }),
36+
new Date(0).toLocaleString(["en"], { era: "narrow" })
37+
);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright (C) 2026 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
/*---
5+
esid: sec-temporal.plaindate.prototype.tolocalestring
6+
description: Temporal.PlainDate should be interpreted and formatted as wall-clock time
7+
features: [Temporal]
8+
---*/
9+
10+
// A non-existent date in the Pacific/Apia timezone.
11+
const instance = Temporal.PlainDate.from({ year: 2011, month: 12, day: 30 });
12+
13+
assert.sameValue(
14+
instance.toLocaleString('en-US', { timeZone: 'Pacific/Apia' }),
15+
'12/30/2011'
16+
);
17+
18+
assert.sameValue(
19+
instance.toLocaleString('en-US', { timeZone: 'Pacific/Apia' }),
20+
instance.toLocaleString('en-US')
21+
);
22+
23+
assert.sameValue(
24+
instance.toLocaleString('en-US', { timeZone: 'Pacific/Apia' }),
25+
instance.toLocaleString('en-US', { timeZone: 'UTC' })
26+
);
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright (C) 2026 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
/*---
5+
esid: sec-temporal.plaindatetime.prototype.tolocalestring
6+
description: Temporal.PlainDateTime should be interpreted and formatted as wall-clock time
7+
features: [Temporal]
8+
---*/
9+
10+
// A non-existent date in the Pacific/Apia timezone.
11+
const instance1 = Temporal.PlainDateTime.from( { year: 2011, month: 12, day: 30 });
12+
13+
assert.sameValue(
14+
instance1.toLocaleString('en-US', { timeZone: 'Pacific/Apia' }),
15+
'12/30/2011, 12:00:00 AM'
16+
);
17+
18+
assert.sameValue(
19+
instance1.toLocaleString('en-US', { timeZone: 'Pacific/Apia' }),
20+
instance1.toLocaleString('en-US')
21+
);
22+
23+
assert.sameValue(
24+
instance1.toLocaleString('en-US', { timeZone: 'Pacific/Apia' }),
25+
instance1.toLocaleString('en-US', { timeZone: 'UTC' })
26+
);
27+
28+
// A non-existent time in the 'America/Los_Angeles' timezone.
29+
const instance2 = new Temporal.PlainDateTime(2026, 3, 8, 2, 30);
30+
31+
assert.sameValue(
32+
instance2.toLocaleString('en-US'),
33+
instance2.toLocaleString('en-US', { timeZone: 'America/Los_Angeles' })
34+
)
35+
36+
assert.sameValue(
37+
instance2.toLocaleString('en-US', { timeZone: 'America/Los_Angeles' }),
38+
'3/8/2026, 2:30:00 AM'
39+
)
40+
41+
assert.sameValue(
42+
instance2.toLocaleString('en-US', { timeZone: 'UTC' }),
43+
instance2.toLocaleString('en-US', { timeZone: 'America/Los_Angeles' })
44+
)
45+
46+
// Creating the instance from a datestring with an offset has no effect.
47+
48+
const instance3 = Temporal.PlainDateTime.from('2026-03-29T02:30:15+01:00');
49+
50+
assert.sameValue(
51+
instance3.toLocaleString('en', { timeStyle: 'long' }),
52+
'2:30:15 AM'
53+
);
54+
55+
assert.sameValue(
56+
instance3.toLocaleString('en', { timeStyle: 'long' }),
57+
instance3.toLocaleString('en', { timeStyle: 'long', timeZone: 'America/New_York' }),
58+
);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright (C) 2026 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
/*---
5+
esid: sec-temporal.plaintime.prototype.tolocalestring
6+
description: Temporal.PlainTime should be interpreted and formatted as wall-clock time
7+
features: [Temporal]
8+
---*/
9+
10+
// A non-existent time in the 'America/Los_Angeles' timezone.
11+
const instance1 = Temporal.PlainTime.from('2026-03-29T02:30:00');
12+
13+
assert.sameValue(
14+
instance1.toLocaleString('en-US'),
15+
instance1.toLocaleString('en-US', { timeZone: 'America/Los_Angeles' })
16+
)
17+
18+
assert.sameValue(
19+
instance1.toLocaleString('en-US', { timeZone: 'America/Los_Angeles' }),
20+
'2:30:00 AM'
21+
)
22+
23+
assert.sameValue(
24+
instance1.toLocaleString('en-US', { timeZone: 'UTC' }),
25+
instance1.toLocaleString('en-US', { timeZone: 'America/Los_Angeles' })
26+
)
27+
28+
// Creating the instance from a datestring with an offset has no effect.
29+
30+
const instance2 = Temporal.PlainTime.from('2026-03-29T02:30:15+01:00');
31+
32+
assert.sameValue(
33+
instance2.toLocaleString('en', { timeStyle: 'long' }),
34+
'2:30:15 AM'
35+
);
36+
37+
assert.sameValue(
38+
instance2.toLocaleString('en', { timeStyle: 'long' }),
39+
instance2.toLocaleString('en', { timeStyle: 'long', timeZone: 'America/New_York' }),
40+
);

0 commit comments

Comments
 (0)