Skip to content

Commit bc02e50

Browse files
committed
Add coverage for remapping of eras in Temporal dates
See note 2 on eras in CalendarResolveFields: https://tc39.es/proposal-temporal/#sec-temporal-calendarresolvefields This behaviour was not yet covered by any test262 tests. Based on Anba's tests from #4080 but with different behaviour.
1 parent cba890e commit bc02e50

File tree

2 files changed

+243
-0
lines changed

2 files changed

+243
-0
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Copyright (C) 2025 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.from
6+
description: Test remapping behaviour of regnal eras
7+
info: |
8+
9+
CalendarDateFromFields:
10+
1. Perform ? CalendarResolveFields(_calendar_, _fields_, _date_).
11+
2. Let result be ? CalendarDateToISO(_calendar_, _fields_, _overflow_).
12+
13+
CalendarResolveFields:
14+
When the fields of _fields_ are inconsistent with respect to a non-unset
15+
_fields_.[[Era]], it is recommended that _fields_.[[Era]] and
16+
_fields_.[[EraYear]] be updated to resolve the inconsistency by lenient
17+
interpretation of out-of-bounds values (rather than throwing a *RangeError*),
18+
which is particularly useful for consistent interpretation of dates in
19+
calendars with regnal eras.
20+
21+
includes: [temporalHelpers.js]
22+
features: [Temporal]
23+
---*/
24+
25+
// Based on a test originally by André Bargull <andre.bargull@gmail.com>
26+
27+
// Notes:
28+
// - "heisei" era started January 8, 1989.
29+
// - "reiwa" era started May 1, 2019.
30+
31+
for (const overflow of ["constrain", "reject"]) {
32+
function test(fields) {
33+
return Temporal.PlainDate.from({ ...fields, calendar: "japanese" }, { overflow });
34+
}
35+
36+
// Reiwa era started in month 5 of the year, so the era of month 1 is remapped
37+
// to be the correct one for the month.
38+
TemporalHelpers.assertPlainDate(
39+
test({ era: "reiwa", eraYear: 1, monthCode: "M01", day: 24 }),
40+
2019, 1, "M01", 24,
41+
"Reiwa 1 before May is mapped to Heisei 31",
42+
"heisei", 31
43+
);
44+
45+
// Reiwa era started on the first day of the month, so day 1 does not need
46+
// remapping.
47+
TemporalHelpers.assertPlainDate(
48+
test({ era: "reiwa", eraYear: 1, monthCode: "M05", day: 1 }),
49+
2019, 5, "M05", 1,
50+
"05-01 Reiwa 1 is not remapped",
51+
"reiwa", 1
52+
);
53+
54+
// Heisei era started on the eighth day of the month. So on previous days the
55+
// era is remapped to be the correct one for the day.
56+
TemporalHelpers.assertPlainDate(
57+
test({ era: "heisei", eraYear: 1, monthCode: "M01", day: 4 }),
58+
1989, 1, "M01", 4,
59+
"01-04 Heisei 1 is remapped to 01-04 Showa 64",
60+
"showa", 64
61+
);
62+
63+
// Era year past the end of the Heisei era is remapped to Reiwa era
64+
TemporalHelpers.assertPlainDate(
65+
test({ era: "heisei", eraYear: 37, monthCode: "M04", day: 25 }),
66+
2025, 4, "M04", 25,
67+
"Heisei 37 is remapped to Reiwa 7",
68+
"reiwa", 7
69+
);
70+
71+
// Zero year in a 1-based era is remapped to the previous era
72+
TemporalHelpers.assertPlainDate(
73+
test({ era: "reiwa", eraYear: 0, monthCode: "M04", day: 25 }),
74+
2018, 4, "M04", 25,
75+
"Reiwa 0 is remapped to Heisei 30",
76+
"heisei", 30
77+
);
78+
79+
// Negative year in a forward-counting era is remapped to the previous era
80+
TemporalHelpers.assertPlainDate(
81+
test({ era: "reiwa", eraYear: -20, monthCode: "M04", day: 25 }),
82+
1998, 4, "M04", 25,
83+
"Reiwa -20 is remapped to Heisei 10",
84+
"heisei", 10
85+
);
86+
87+
// Test the last two things for Gregorian eras as well
88+
function testGregorian(fields) {
89+
return Temporal.PlainDate.from({ ...fields, calendar: "gregory" }, { overflow });
90+
}
91+
TemporalHelpers.assertPlainDate(
92+
testGregorian({ era: "ce", eraYear: 0, monthCode: "M04", day: 25 }),
93+
0, 4, "M04", 25,
94+
"0 CE is remapped to 1 BCE",
95+
"bce", 1
96+
);
97+
TemporalHelpers.assertPlainDate(
98+
testGregorian({ era: "ce", eraYear: -20, monthCode: "M04", day: 25 }),
99+
-20, 4, "M04", 25,
100+
"-20 CE is remapped to 21 BCE",
101+
"bce", 21
102+
);
103+
104+
// Zero year in a backwards-counting era is remapped to the next era
105+
TemporalHelpers.assertPlainDate(
106+
testGregorian({ era: "bce", eraYear: 0, monthCode: "M04", day: 25 }),
107+
1, 4, "M04", 25,
108+
"0 BCE is remapped to 1 CE",
109+
"ce", 1
110+
);
111+
112+
// Negative year in a backwards-counting era is remapped to the next era
113+
TemporalHelpers.assertPlainDate(
114+
testGregorian({ era: "bce", eraYear: -20, monthCode: "M04", day: 25 }),
115+
21, 4, "M04", 25,
116+
"-20 BCE is remapped to 21 CE",
117+
"ce", 21
118+
);
119+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// Copyright (C) 2025 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.plainyearmonth.from
6+
description: Test remapping behaviour of regnal eras
7+
info: |
8+
9+
CalendarYearMonthFromFields:
10+
1. Perform ? CalendarResolveFields(_calendar_, _fields_, ~year-month~).
11+
2. Let _firstDayIndex_ be the 1-based index of the first day of the month
12+
described by _fields_ (i.e., 1 unless the month's first day is skipped by
13+
this calendar.)
14+
3. Set _fields_.[[Day]] to _firstDayIndex_.
15+
4. Let result be ? CalendarDateToISO(_calendar_, _fields_, _overflow_).
16+
17+
CalendarResolveFields:
18+
When the fields of _fields_ are inconsistent with respect to a non-unset
19+
_fields_.[[Era]], it is recommended that _fields_.[[Era]] and
20+
_fields_.[[EraYear]] be updated to resolve the inconsistency by lenient
21+
interpretation of out-of-bounds values (rather than throwing a *RangeError*),
22+
which is particularly useful for consistent interpretation of dates in
23+
calendars with regnal eras.
24+
25+
includes: [temporalHelpers.js]
26+
features: [Temporal]
27+
---*/
28+
29+
// Based on a test originally by André Bargull <andre.bargull@gmail.com>
30+
31+
// Notes:
32+
// - "heisei" era started January 8, 1989.
33+
// - "reiwa" era started May 1, 2019.
34+
35+
for (const overflow of ["constrain", "reject"]) {
36+
function test(fields) {
37+
return Temporal.PlainYearMonth.from({ ...fields, calendar: "japanese" }, { overflow });
38+
}
39+
40+
// Reiwa era started in month 5 of the year, so the era of month 1 is remapped
41+
// to be the correct one for the month.
42+
TemporalHelpers.assertPlainYearMonth(
43+
test({ era: "reiwa", eraYear: 1, monthCode: "M01" }),
44+
2019, 1, "M01",
45+
"Reiwa 1 before May is mapped to Heisei 31",
46+
"heisei", 31, /* reference day = */ 1
47+
);
48+
49+
// Reiwa era started on the first day of the month, so the reference day 1
50+
// does not need remapping.
51+
TemporalHelpers.assertPlainYearMonth(
52+
test({ era: "reiwa", eraYear: 1, monthCode: "M05" }),
53+
2019, 5, "M05",
54+
"reference day is 1",
55+
"reiwa", 1, /* reference day = */ 1
56+
);
57+
58+
// Heisei era started on the eighth day of the month, but PlainYearMonth
59+
// references the first day of the month. So the era is remapped to be the
60+
// correct one for the reference day.
61+
TemporalHelpers.assertPlainYearMonth(
62+
test({ era: "heisei", eraYear: 1, monthCode: "M01" }),
63+
1989, 1, "M01",
64+
"01-01 Heisei 1 is remapped to 01-01 Showa 64",
65+
"showa", 64, /* reference day = */ 1
66+
);
67+
68+
// Era year past the end of the Heisei era is remapped to Reiwa era
69+
TemporalHelpers.assertPlainYearMonth(
70+
test({ era: "heisei", eraYear: 37, monthCode: "M04" }),
71+
2025, 4, "M04",
72+
"Heisei 37 is remapped to Reiwa 7",
73+
"reiwa", 7, /* reference day = */ 1
74+
);
75+
76+
// Zero year in a 1-based era is remapped to the previous era
77+
TemporalHelpers.assertPlainYearMonth(
78+
test({ era: "reiwa", eraYear: 0, monthCode: "M04" }),
79+
2018, 4, "M04",
80+
"Reiwa 0 is remapped to Heisei 30",
81+
"heisei", 30, /* reference day = */ 1
82+
);
83+
84+
// Negative year in a forwards-counting era is remapped to the previous era
85+
TemporalHelpers.assertPlainYearMonth(
86+
test({ era: "reiwa", eraYear: -20, monthCode: "M04" }),
87+
1998, 4, "M04",
88+
"Reiwa -20 is remapped to Heisei 10",
89+
"heisei", 10, /* reference day = */ 1
90+
);
91+
92+
// Test the last two things for Gregorian eras as well
93+
function testGregorian(fields) {
94+
return Temporal.PlainYearMonth.from({ ...fields, calendar: "gregory" }, { overflow });
95+
}
96+
TemporalHelpers.assertPlainYearMonth(
97+
testGregorian({ era: "ce", eraYear: 0, monthCode: "M04" }),
98+
0, 4, "M04",
99+
"0 CE is remapped to 1 BCE",
100+
"bce", 1, /* reference day = */ 1
101+
);
102+
TemporalHelpers.assertPlainYearMonth(
103+
testGregorian({ era: "ce", eraYear: -20, monthCode: "M04" }),
104+
-20, 4, "M04",
105+
"-20 CE is remapped to 21 BCE",
106+
"bce", 21, /* reference day = */ 1
107+
);
108+
109+
// Zero year in a backwards-counting era is remapped to the next era
110+
TemporalHelpers.assertPlainYearMonth(
111+
testGregorian({ era: "bce", eraYear: 0, monthCode: "M04" }),
112+
1, 4, "M04",
113+
"0 BCE is remapped to 1 CE",
114+
"ce", 1, /* reference day = */ 1
115+
);
116+
117+
// Negative year in a backwards-counting era is remapped to the next era
118+
TemporalHelpers.assertPlainYearMonth(
119+
testGregorian({ era: "bce", eraYear: -20, monthCode: "M04" }),
120+
21, 4, "M04",
121+
"-20 BCE is remapped to 21 CE",
122+
"ce", 21, /* reference day = */ 1
123+
);
124+
}

0 commit comments

Comments
 (0)