Intl Era Monthcode: Arithmetic year tests for non-ISO calendars

Tests the spec logic of determining the epoch year, and spot-checks
various other values of the arithmetic year given an era and era year.
This commit is contained in:
Philip Chimento 2025-11-15 14:07:17 -08:00 committed by Ms2ger
parent 8d2fba2d16
commit 8c87fb920b
8 changed files with 739 additions and 0 deletions

View File

@ -0,0 +1,131 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.year
description: Arithmetic year calculations for all non-ISO8601 calendars
info: |
4.1.12 CalendarDateArithmeticYear ( _calendar_, _date_ )
1. Let _r_ be the row in Table 4 which the value of the Calendar column is
_calendar_.
2. Let _epochYear_ be the value given in the "Epoch ISO Year" column of _r_.
3. Let _epochDate_ be the first day of the calendar year starting in ISO year
epochYear in the calendar represented by _calendar_, according to
implementation-defined processing.
4. Let _newYear_ be the first day of the calendar year of _date_ in the
calendar represented by _calendar_, according to implementation-defined
processing.
5. Let _arithmeticYear_ be the number of whole years between _epochDate_ and
_newYear_ in the calendar represented by _calendar_, according to
implementation-defined processing.
6. Return _arithmeticYear_.
features: [Temporal, Intl.Era-monthcode]
---*/
// Note: year 0 is tested in epoch-year.js
const tests = {
// One era; arithmetic year equals era year
buddhist: [
[{ era: "be", eraYear: -1, monthCode: "M06", day: 27 }, -1],
[{ era: "be", eraYear: 1, monthCode: "M11", day: 13 }, 1],
// 2483 would be a short year if the calendar was incorrectly non-proleptic
[{ era: "be", eraYear: 2483, monthCode: "M02", day: 15 }, 2483],
[{ era: "be", eraYear: 2567, monthCode: "M08", day: 4 }, 2567],
],
// No eras; we just test that we get back the same arithmetic year we gave
chinese: [
[{ year: 2025, monthCode: "M09", day: 26 }, 2025],
],
// One era; arithmetic year equals era year
coptic: [
[{ era: "am", eraYear: -1, monthCode: "M04", day: 11 }, -1],
[{ era: "am", eraYear: 1, monthCode: "M01", day: 12 }, 1],
[{ era: "am", eraYear: 1742, monthCode: "M03", day: 6 }, 1742],
],
// No eras; we just test that we get back the same arithmetic year we gave
dangi: [
[{ year: 2025, monthCode: "M09", day: 26 }, 2025],
],
// One era; arithmetic year equals era year
ethioaa: [
[{ era: "aa", eraYear: -1, monthCode: "M04", day: 11 }, -1],
[{ era: "aa", eraYear: 1, monthCode: "M01", day: 12 }, 1],
[{ era: "aa", eraYear: 7518, monthCode: "M03", day: 6 }, 7518],
],
ethiopic: [
[{ era: "aa", eraYear: -1, monthCode: "M02", day: 21 }, -5501],
[{ era: "aa", eraYear: 0, monthCode: "M04", day: 20 }, -5500],
[{ era: "aa", eraYear: 1, monthCode: "M13", day: 5 }, -5499],
[{ era: "aa", eraYear: 5499, monthCode: "M11", day: 16 }, -1],
[{ era: "am", eraYear: 1, monthCode: "M07", day: 24 }, 1],
[{ era: "am", eraYear: 2018, monthCode: "M03", day: 6 }, 2018],
],
gregory: [
[{ era: "bce", eraYear: 2, monthCode: "M06", day: 14 }, -1],
[{ era: "bce", eraYear: 1, monthCode: "M12", day: 3 }, 0],
[{ era: "ce", eraYear: 1, monthCode: "M07", day: 26 }, 1],
[{ era: "ce", eraYear: 2025, monthCode: "M11", day: 15 }, 2025],
],
// One era; arithmetic year equals era year
hebrew: [
[{ era: "am", eraYear: -1, monthCode: "M06", day: 2 }, -1], // fails
[{ era: "am", eraYear: 1, monthCode: "M09", day: 24 }, 1],
[{ era: "am", eraYear: 5786, monthCode: "M02", day: 24 }, 5786],
],
// One era; arithmetic year equals era year
indian: [
[{ era: "shaka", eraYear: -1, monthCode: "M03", day: 31 }, -1],
[{ era: "shaka", eraYear: 1, monthCode: "M01", day: 6 }, 1],
[{ era: "shaka", eraYear: 1947, monthCode: "M08", day: 24 }, 1947],
],
"islamic-civil": [
[{ era: "bh", eraYear: 2, monthCode: "M08", day: 24 }, -1],
[{ era: "bh", eraYear: 1, monthCode: "M01", day: 6 }, 0],
[{ era: "ah", eraYear: 1, monthCode: "M01", day: 5 }, 1],
[{ era: "ah", eraYear: 1447, monthCode: "M05", day: 24 }, 1447],
],
"islamic-tbla": [
[{ era: "bh", eraYear: 2, monthCode: "M05", day: 19 }, -1],
[{ era: "bh", eraYear: 1, monthCode: "M10", day: 16 }, 0],
[{ era: "ah", eraYear: 1, monthCode: "M12", day: 7 }, 1],
[{ era: "ah", eraYear: 1447, monthCode: "M05", day: 25 }, 1447],
],
"islamic-umalqura": [
[{ era: "bh", eraYear: 2, monthCode: "M09", day: 27 }, -1],
[{ era: "bh", eraYear: 1, monthCode: "M07", day: 17 }, 0],
[{ era: "ah", eraYear: 1, monthCode: "M11", day: 25 }, 1],
[{ era: "ah", eraYear: 1447, monthCode: "M05", day: 24 }, 1447],
],
japanese: [
[{ era: "bce", eraYear: 2, monthCode: "M06", day: 14 }, -1],
[{ era: "bce", eraYear: 1, monthCode: "M12", day: 3 }, 0],
[{ era: "ce", eraYear: 1, monthCode: "M07", day: 26 }, 1],
[{ era: "meiji", eraYear: 1, monthCode: "M12", day: 31 }, 1868],
[{ era: "taisho", eraYear: 1, monthCode: "M12", day: 31 }, 1912],
[{ era: "showa", eraYear: 1, monthCode: "M12", day: 31 }, 1926],
[{ era: "heisei", eraYear: 1, monthCode: "M12", day: 31 }, 1989],
[{ era: "reiwa", eraYear: 1, monthCode: "M12", day: 31 }, 2019],
[{ era: "reiwa", eraYear: 7, monthCode: "M11", day: 15 }, 2025],
],
// One era; arithmetic year equals era year
persian: [
[{ era: "ap", eraYear: -1, monthCode: "M09", day: 25 }, -1],
[{ era: "ap", eraYear: 1, monthCode: "M08", day: 22 }, 1],
[{ era: "ap", eraYear: 1404, monthCode: "M08", day: 24 }, 1404],
],
roc: [
[{ era: "broc", eraYear: 2, monthCode: "M09", day: 25 }, -1],
[{ era: "broc", eraYear: 1, monthCode: "M05", day: 3 }, 0],
[{ era: "roc", eraYear: 1, monthCode: "M12", day: 18 }, 1],
[{ era: "roc", eraYear: 114, monthCode: "M11", day: 15 }, 114],
],
};
for (const [calendar, cases] of Object.entries(tests)) {
for (const [fromArgs, expectedYear] of cases) {
const date = Temporal.PlainDate.from({ ...fromArgs, calendar }, { overflow: "reject" });
assert.sameValue(date.year, expectedYear, `${date} should have arithmetic year ${expectedYear}`);
}
}

View File

@ -0,0 +1,53 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.year
description: >
Determination of the epoch year for arithmetic years for all non-ISO8601
calendars
info: |
4.1.12 CalendarDateArithmeticYear ( _calendar_, _date_ )
1. Let _r_ be the row in Table 4 which the value of the Calendar column is
_calendar_.
2. Let _epochYear_ be the value given in the "Epoch ISO Year" column of _r_.
3. Let _epochDate_ be the first day of the calendar year starting in ISO year
epochYear in the calendar represented by _calendar_, according to
implementation-defined processing.
4. Let _newYear_ be the first day of the calendar year of _date_ in the
calendar represented by _calendar_, according to implementation-defined
processing.
5. Let _arithmeticYear_ be the number of whole years between _epochDate_ and
_newYear_ in the calendar represented by _calendar_, according to
implementation-defined processing.
6. Return _arithmeticYear_.
features: [Temporal, Intl.Era-monthcode]
---*/
const epochYears = {
buddhist: -543,
// Chinese calendar omitted, in order to avoid creating an instance outside
// the well-defined range
coptic: 283,
// Dangi calendar omitted, see above
ethioaa: -5493,
ethiopic: 7,
gregory: 0,
hebrew: -3761,
indian: 78,
"islamic-civil": 621,
"islamic-tbla": 621,
"islamic-umalqura": 621,
japanese: 0,
persian: 621,
roc: 1911,
};
for (const [calendar, epochYear] of Object.entries(epochYears)) {
const epochDate = new Temporal.PlainDate(epochYear, 12, 31)
.withCalendar(calendar)
.with({ monthCode: "M01", day: 1 });
assert.sameValue(epochDate.year, 0, `${calendar} arithmetic year 0 should start in ISO year ${epochYear}`);
}

View File

@ -0,0 +1,131 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.year
description: Arithmetic year calculations for all non-ISO8601 calendars
info: |
4.1.12 CalendarDateArithmeticYear ( _calendar_, _date_ )
1. Let _r_ be the row in Table 4 which the value of the Calendar column is
_calendar_.
2. Let _epochYear_ be the value given in the "Epoch ISO Year" column of _r_.
3. Let _epochDate_ be the first day of the calendar year starting in ISO year
epochYear in the calendar represented by _calendar_, according to
implementation-defined processing.
4. Let _newYear_ be the first day of the calendar year of _date_ in the
calendar represented by _calendar_, according to implementation-defined
processing.
5. Let _arithmeticYear_ be the number of whole years between _epochDate_ and
_newYear_ in the calendar represented by _calendar_, according to
implementation-defined processing.
6. Return _arithmeticYear_.
features: [Temporal, Intl.Era-monthcode]
---*/
// Note: year 0 is tested in epoch-year.js
const tests = {
// One era; arithmetic year equals era year
buddhist: [
[{ era: "be", eraYear: -1, monthCode: "M06", day: 27 }, -1],
[{ era: "be", eraYear: 1, monthCode: "M11", day: 13 }, 1],
// 2483 would be a short year if the calendar was incorrectly non-proleptic
[{ era: "be", eraYear: 2483, monthCode: "M02", day: 15 }, 2483],
[{ era: "be", eraYear: 2567, monthCode: "M08", day: 4 }, 2567],
],
// No eras; we just test that we get back the same arithmetic year we gave
chinese: [
[{ year: 2025, monthCode: "M09", day: 26 }, 2025],
],
// One era; arithmetic year equals era year
coptic: [
[{ era: "am", eraYear: -1, monthCode: "M04", day: 11 }, -1],
[{ era: "am", eraYear: 1, monthCode: "M01", day: 12 }, 1],
[{ era: "am", eraYear: 1742, monthCode: "M03", day: 6 }, 1742],
],
// No eras; we just test that we get back the same arithmetic year we gave
dangi: [
[{ year: 2025, monthCode: "M09", day: 26 }, 2025],
],
// One era; arithmetic year equals era year
ethioaa: [
[{ era: "aa", eraYear: -1, monthCode: "M04", day: 11 }, -1],
[{ era: "aa", eraYear: 1, monthCode: "M01", day: 12 }, 1],
[{ era: "aa", eraYear: 7518, monthCode: "M03", day: 6 }, 7518],
],
ethiopic: [
[{ era: "aa", eraYear: -1, monthCode: "M02", day: 21 }, -5501],
[{ era: "aa", eraYear: 0, monthCode: "M04", day: 20 }, -5500],
[{ era: "aa", eraYear: 1, monthCode: "M13", day: 5 }, -5499],
[{ era: "aa", eraYear: 5499, monthCode: "M11", day: 16 }, -1],
[{ era: "am", eraYear: 1, monthCode: "M07", day: 24 }, 1],
[{ era: "am", eraYear: 2018, monthCode: "M03", day: 6 }, 2018],
],
gregory: [
[{ era: "bce", eraYear: 2, monthCode: "M06", day: 14 }, -1],
[{ era: "bce", eraYear: 1, monthCode: "M12", day: 3 }, 0],
[{ era: "ce", eraYear: 1, monthCode: "M07", day: 26 }, 1],
[{ era: "ce", eraYear: 2025, monthCode: "M11", day: 15 }, 2025],
],
// One era; arithmetic year equals era year
hebrew: [
[{ era: "am", eraYear: -1, monthCode: "M06", day: 2 }, -1], // fails
[{ era: "am", eraYear: 1, monthCode: "M09", day: 24 }, 1],
[{ era: "am", eraYear: 5786, monthCode: "M02", day: 24 }, 5786],
],
// One era; arithmetic year equals era year
indian: [
[{ era: "shaka", eraYear: -1, monthCode: "M03", day: 31 }, -1],
[{ era: "shaka", eraYear: 1, monthCode: "M01", day: 6 }, 1],
[{ era: "shaka", eraYear: 1947, monthCode: "M08", day: 24 }, 1947],
],
"islamic-civil": [
[{ era: "bh", eraYear: 2, monthCode: "M08", day: 24 }, -1],
[{ era: "bh", eraYear: 1, monthCode: "M01", day: 6 }, 0],
[{ era: "ah", eraYear: 1, monthCode: "M01", day: 5 }, 1],
[{ era: "ah", eraYear: 1447, monthCode: "M05", day: 24 }, 1447],
],
"islamic-tbla": [
[{ era: "bh", eraYear: 2, monthCode: "M05", day: 19 }, -1],
[{ era: "bh", eraYear: 1, monthCode: "M10", day: 16 }, 0],
[{ era: "ah", eraYear: 1, monthCode: "M12", day: 7 }, 1],
[{ era: "ah", eraYear: 1447, monthCode: "M05", day: 25 }, 1447],
],
"islamic-umalqura": [
[{ era: "bh", eraYear: 2, monthCode: "M09", day: 27 }, -1],
[{ era: "bh", eraYear: 1, monthCode: "M07", day: 17 }, 0],
[{ era: "ah", eraYear: 1, monthCode: "M11", day: 25 }, 1],
[{ era: "ah", eraYear: 1447, monthCode: "M05", day: 24 }, 1447],
],
japanese: [
[{ era: "bce", eraYear: 2, monthCode: "M06", day: 14 }, -1],
[{ era: "bce", eraYear: 1, monthCode: "M12", day: 3 }, 0],
[{ era: "ce", eraYear: 1, monthCode: "M07", day: 26 }, 1],
[{ era: "meiji", eraYear: 1, monthCode: "M12", day: 31 }, 1868],
[{ era: "taisho", eraYear: 1, monthCode: "M12", day: 31 }, 1912],
[{ era: "showa", eraYear: 1, monthCode: "M12", day: 31 }, 1926],
[{ era: "heisei", eraYear: 1, monthCode: "M12", day: 31 }, 1989],
[{ era: "reiwa", eraYear: 1, monthCode: "M12", day: 31 }, 2019],
[{ era: "reiwa", eraYear: 7, monthCode: "M11", day: 15 }, 2025],
],
// One era; arithmetic year equals era year
persian: [
[{ era: "ap", eraYear: -1, monthCode: "M09", day: 25 }, -1],
[{ era: "ap", eraYear: 1, monthCode: "M08", day: 22 }, 1],
[{ era: "ap", eraYear: 1404, monthCode: "M08", day: 24 }, 1404],
],
roc: [
[{ era: "broc", eraYear: 2, monthCode: "M09", day: 25 }, -1],
[{ era: "broc", eraYear: 1, monthCode: "M05", day: 3 }, 0],
[{ era: "roc", eraYear: 1, monthCode: "M12", day: 18 }, 1],
[{ era: "roc", eraYear: 114, monthCode: "M11", day: 15 }, 114],
],
};
for (const [calendar, cases] of Object.entries(tests)) {
for (const [fromArgs, expectedYear] of cases) {
const date = Temporal.PlainDateTime.from({ ...fromArgs, hour: 12, minute: 34, calendar }, { overflow: "reject" });
assert.sameValue(date.year, expectedYear, `${date} should have arithmetic year ${expectedYear}`);
}
}

View File

@ -0,0 +1,54 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.year
description: >
Determination of the epoch year for arithmetic years for all non-ISO8601
calendars
info: |
4.1.12 CalendarDateArithmeticYear ( _calendar_, _date_ )
1. Let _r_ be the row in Table 4 which the value of the Calendar column is
_calendar_.
2. Let _epochYear_ be the value given in the "Epoch ISO Year" column of _r_.
3. Let _epochDate_ be the first day of the calendar year starting in ISO year
epochYear in the calendar represented by _calendar_, according to
implementation-defined processing.
4. Let _newYear_ be the first day of the calendar year of _date_ in the
calendar represented by _calendar_, according to implementation-defined
processing.
5. Let _arithmeticYear_ be the number of whole years between _epochDate_ and
_newYear_ in the calendar represented by _calendar_, according to
implementation-defined processing.
6. Return _arithmeticYear_.
features: [Temporal, Intl.Era-monthcode]
---*/
const epochYears = {
buddhist: -543,
// Chinese calendar omitted, in order to avoid creating an instance outside
// the well-defined range
coptic: 283,
// Dangi calendar omitted, see above
ethioaa: -5493,
ethiopic: 7,
gregory: 0,
hebrew: -3761,
indian: 78,
"islamic-civil": 621,
"islamic-tbla": 621,
"islamic-umalqura": 621,
japanese: 0,
persian: 621,
roc: 1911,
};
for (const [calendar, epochYear] of Object.entries(epochYears)) {
const epochDate = new Temporal.PlainDate(epochYear, 12, 31)
.withCalendar(calendar)
.with({ monthCode: "M01", day: 1 })
.toPlainDateTime();
assert.sameValue(epochDate.year, 0, `${calendar} arithmetic year 0 should start in ISO year ${epochYear}`);
}

View File

@ -0,0 +1,131 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plainyearmonth.prototype.year
description: Arithmetic year calculations for all non-ISO8601 calendars
info: |
4.1.12 CalendarDateArithmeticYear ( _calendar_, _date_ )
1. Let _r_ be the row in Table 4 which the value of the Calendar column is
_calendar_.
2. Let _epochYear_ be the value given in the "Epoch ISO Year" column of _r_.
3. Let _epochDate_ be the first day of the calendar year starting in ISO year
epochYear in the calendar represented by _calendar_, according to
implementation-defined processing.
4. Let _newYear_ be the first day of the calendar year of _date_ in the
calendar represented by _calendar_, according to implementation-defined
processing.
5. Let _arithmeticYear_ be the number of whole years between _epochDate_ and
_newYear_ in the calendar represented by _calendar_, according to
implementation-defined processing.
6. Return _arithmeticYear_.
features: [Temporal, Intl.Era-monthcode]
---*/
// Note: year 0 is tested in epoch-year.js
const tests = {
// One era; arithmetic year equals era year
buddhist: [
[{ era: "be", eraYear: -1, monthCode: "M06" }, -1],
[{ era: "be", eraYear: 1, monthCode: "M11" }, 1],
// 2483 would be a short year if the calendar was incorrectly non-proleptic
[{ era: "be", eraYear: 2483, monthCode: "M02" }, 2483],
[{ era: "be", eraYear: 2567, monthCode: "M08" }, 2567],
],
// No eras; we just test that we get back the same arithmetic year we gave
chinese: [
[{ year: 2025, monthCode: "M09" }, 2025],
],
// One era; arithmetic year equals era year
coptic: [
[{ era: "am", eraYear: -1, monthCode: "M04" }, -1],
[{ era: "am", eraYear: 1, monthCode: "M01" }, 1],
[{ era: "am", eraYear: 1742, monthCode: "M03" }, 1742],
],
// No eras; we just test that we get back the same arithmetic year we gave
dangi: [
[{ year: 2025, monthCode: "M09" }, 2025],
],
// One era; arithmetic year equals era year
ethioaa: [
[{ era: "aa", eraYear: -1, monthCode: "M04" }, -1],
[{ era: "aa", eraYear: 1, monthCode: "M01" }, 1],
[{ era: "aa", eraYear: 7518, monthCode: "M03" }, 7518],
],
ethiopic: [
[{ era: "aa", eraYear: -1, monthCode: "M02" }, -5501],
[{ era: "aa", eraYear: 0, monthCode: "M04" }, -5500],
[{ era: "aa", eraYear: 1, monthCode: "M13" }, -5499],
[{ era: "aa", eraYear: 5499, monthCode: "M11" }, -1],
[{ era: "am", eraYear: 1, monthCode: "M07" }, 1],
[{ era: "am", eraYear: 2018, monthCode: "M03" }, 2018],
],
gregory: [
[{ era: "bce", eraYear: 2, monthCode: "M06" }, -1],
[{ era: "bce", eraYear: 1, monthCode: "M12" }, 0],
[{ era: "ce", eraYear: 1, monthCode: "M07" }, 1],
[{ era: "ce", eraYear: 2025, monthCode: "M11" }, 2025],
],
// One era; arithmetic year equals era year
hebrew: [
[{ era: "am", eraYear: -1, monthCode: "M06" }, -1], // fails
[{ era: "am", eraYear: 1, monthCode: "M09" }, 1],
[{ era: "am", eraYear: 5786, monthCode: "M02" }, 5786],
],
// One era; arithmetic year equals era year
indian: [
[{ era: "shaka", eraYear: -1, monthCode: "M03" }, -1],
[{ era: "shaka", eraYear: 1, monthCode: "M01" }, 1],
[{ era: "shaka", eraYear: 1947, monthCode: "M08" }, 1947],
],
"islamic-civil": [
[{ era: "bh", eraYear: 2, monthCode: "M08" }, -1],
[{ era: "bh", eraYear: 1, monthCode: "M01" }, 0],
[{ era: "ah", eraYear: 1, monthCode: "M01" }, 1],
[{ era: "ah", eraYear: 1447, monthCode: "M05" }, 1447],
],
"islamic-tbla": [
[{ era: "bh", eraYear: 2, monthCode: "M05" }, -1],
[{ era: "bh", eraYear: 1, monthCode: "M10" }, 0],
[{ era: "ah", eraYear: 1, monthCode: "M12" }, 1],
[{ era: "ah", eraYear: 1447, monthCode: "M05" }, 1447],
],
"islamic-umalqura": [
[{ era: "bh", eraYear: 2, monthCode: "M09" }, -1],
[{ era: "bh", eraYear: 1, monthCode: "M07" }, 0],
[{ era: "ah", eraYear: 1, monthCode: "M11" }, 1],
[{ era: "ah", eraYear: 1447, monthCode: "M05" }, 1447],
],
japanese: [
[{ era: "bce", eraYear: 2, monthCode: "M06" }, -1],
[{ era: "bce", eraYear: 1, monthCode: "M12" }, 0],
[{ era: "ce", eraYear: 1, monthCode: "M07" }, 1],
[{ era: "meiji", eraYear: 1, monthCode: "M12" }, 1868],
[{ era: "taisho", eraYear: 1, monthCode: "M12" }, 1912],
[{ era: "showa", eraYear: 1, monthCode: "M12" }, 1926],
[{ era: "heisei", eraYear: 1, monthCode: "M12" }, 1989],
[{ era: "reiwa", eraYear: 1, monthCode: "M12" }, 2019],
[{ era: "reiwa", eraYear: 7, monthCode: "M11" }, 2025],
],
// One era; arithmetic year equals era year
persian: [
[{ era: "ap", eraYear: -1, monthCode: "M09" }, -1],
[{ era: "ap", eraYear: 1, monthCode: "M08" }, 1],
[{ era: "ap", eraYear: 1404, monthCode: "M08" }, 1404],
],
roc: [
[{ era: "broc", eraYear: 2, monthCode: "M09" }, -1],
[{ era: "broc", eraYear: 1, monthCode: "M05" }, 0],
[{ era: "roc", eraYear: 1, monthCode: "M12" }, 1],
[{ era: "roc", eraYear: 114, monthCode: "M11" }, 114],
],
};
for (const [calendar, cases] of Object.entries(tests)) {
for (const [fromArgs, expectedYear] of cases) {
const date = Temporal.PlainYearMonth.from({ ...fromArgs, calendar }, { overflow: "reject" });
assert.sameValue(date.year, expectedYear, `${date} should have arithmetic year ${expectedYear}`);
}
}

View File

@ -0,0 +1,54 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plainyearmonth.prototype.year
description: >
Determination of the epoch year for arithmetic years for all non-ISO8601
calendars
info: |
4.1.12 CalendarDateArithmeticYear ( _calendar_, _date_ )
1. Let _r_ be the row in Table 4 which the value of the Calendar column is
_calendar_.
2. Let _epochYear_ be the value given in the "Epoch ISO Year" column of _r_.
3. Let _epochDate_ be the first day of the calendar year starting in ISO year
epochYear in the calendar represented by _calendar_, according to
implementation-defined processing.
4. Let _newYear_ be the first day of the calendar year of _date_ in the
calendar represented by _calendar_, according to implementation-defined
processing.
5. Let _arithmeticYear_ be the number of whole years between _epochDate_ and
_newYear_ in the calendar represented by _calendar_, according to
implementation-defined processing.
6. Return _arithmeticYear_.
features: [Temporal, Intl.Era-monthcode]
---*/
const epochYears = {
buddhist: -543,
// Chinese calendar omitted, in order to avoid creating an instance outside
// the well-defined range
coptic: 283,
// Dangi calendar omitted, see above
ethioaa: -5493,
ethiopic: 7,
gregory: 0,
hebrew: -3761,
indian: 78,
"islamic-civil": 621,
"islamic-tbla": 621,
"islamic-umalqura": 621,
japanese: 0,
persian: 621,
roc: 1911,
};
for (const [calendar, epochYear] of Object.entries(epochYears)) {
const epochDate = new Temporal.PlainDate(epochYear, 12, 31)
.withCalendar(calendar)
.with({ monthCode: "M01", day: 1 })
.toPlainYearMonth();
assert.sameValue(epochDate.year, 0, `${calendar} arithmetic year 0 should start in ISO year ${epochYear}`);
}

View File

@ -0,0 +1,131 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.year
description: Arithmetic year calculations for all non-ISO8601 calendars
info: |
4.1.12 CalendarDateArithmeticYear ( _calendar_, _date_ )
1. Let _r_ be the row in Table 4 which the value of the Calendar column is
_calendar_.
2. Let _epochYear_ be the value given in the "Epoch ISO Year" column of _r_.
3. Let _epochDate_ be the first day of the calendar year starting in ISO year
epochYear in the calendar represented by _calendar_, according to
implementation-defined processing.
4. Let _newYear_ be the first day of the calendar year of _date_ in the
calendar represented by _calendar_, according to implementation-defined
processing.
5. Let _arithmeticYear_ be the number of whole years between _epochDate_ and
_newYear_ in the calendar represented by _calendar_, according to
implementation-defined processing.
6. Return _arithmeticYear_.
features: [Temporal, Intl.Era-monthcode]
---*/
// Note: year 0 is tested in epoch-year.js
const tests = {
// One era; arithmetic year equals era year
buddhist: [
[{ era: "be", eraYear: -1, monthCode: "M06", day: 27 }, -1],
[{ era: "be", eraYear: 1, monthCode: "M11", day: 13 }, 1],
// 2483 would be a short year if the calendar was incorrectly non-proleptic
[{ era: "be", eraYear: 2483, monthCode: "M02", day: 15 }, 2483],
[{ era: "be", eraYear: 2567, monthCode: "M08", day: 4 }, 2567],
],
// No eras; we just test that we get back the same arithmetic year we gave
chinese: [
[{ year: 2025, monthCode: "M09", day: 26 }, 2025],
],
// One era; arithmetic year equals era year
coptic: [
[{ era: "am", eraYear: -1, monthCode: "M04", day: 11 }, -1],
[{ era: "am", eraYear: 1, monthCode: "M01", day: 12 }, 1],
[{ era: "am", eraYear: 1742, monthCode: "M03", day: 6 }, 1742],
],
// No eras; we just test that we get back the same arithmetic year we gave
dangi: [
[{ year: 2025, monthCode: "M09", day: 26 }, 2025],
],
// One era; arithmetic year equals era year
ethioaa: [
[{ era: "aa", eraYear: -1, monthCode: "M04", day: 11 }, -1],
[{ era: "aa", eraYear: 1, monthCode: "M01", day: 12 }, 1],
[{ era: "aa", eraYear: 7518, monthCode: "M03", day: 6 }, 7518],
],
ethiopic: [
[{ era: "aa", eraYear: -1, monthCode: "M02", day: 21 }, -5501],
[{ era: "aa", eraYear: 0, monthCode: "M04", day: 20 }, -5500],
[{ era: "aa", eraYear: 1, monthCode: "M13", day: 5 }, -5499],
[{ era: "aa", eraYear: 5499, monthCode: "M11", day: 16 }, -1],
[{ era: "am", eraYear: 1, monthCode: "M07", day: 24 }, 1],
[{ era: "am", eraYear: 2018, monthCode: "M03", day: 6 }, 2018],
],
gregory: [
[{ era: "bce", eraYear: 2, monthCode: "M06", day: 14 }, -1],
[{ era: "bce", eraYear: 1, monthCode: "M12", day: 3 }, 0],
[{ era: "ce", eraYear: 1, monthCode: "M07", day: 26 }, 1],
[{ era: "ce", eraYear: 2025, monthCode: "M11", day: 15 }, 2025],
],
// One era; arithmetic year equals era year
hebrew: [
[{ era: "am", eraYear: -1, monthCode: "M06", day: 2 }, -1], // fails
[{ era: "am", eraYear: 1, monthCode: "M09", day: 24 }, 1],
[{ era: "am", eraYear: 5786, monthCode: "M02", day: 24 }, 5786],
],
// One era; arithmetic year equals era year
indian: [
[{ era: "shaka", eraYear: -1, monthCode: "M03", day: 31 }, -1],
[{ era: "shaka", eraYear: 1, monthCode: "M01", day: 6 }, 1],
[{ era: "shaka", eraYear: 1947, monthCode: "M08", day: 24 }, 1947],
],
"islamic-civil": [
[{ era: "bh", eraYear: 2, monthCode: "M08", day: 24 }, -1],
[{ era: "bh", eraYear: 1, monthCode: "M01", day: 6 }, 0],
[{ era: "ah", eraYear: 1, monthCode: "M01", day: 5 }, 1],
[{ era: "ah", eraYear: 1447, monthCode: "M05", day: 24 }, 1447],
],
"islamic-tbla": [
[{ era: "bh", eraYear: 2, monthCode: "M05", day: 19 }, -1],
[{ era: "bh", eraYear: 1, monthCode: "M10", day: 16 }, 0],
[{ era: "ah", eraYear: 1, monthCode: "M12", day: 7 }, 1],
[{ era: "ah", eraYear: 1447, monthCode: "M05", day: 25 }, 1447],
],
"islamic-umalqura": [
[{ era: "bh", eraYear: 2, monthCode: "M09", day: 27 }, -1],
[{ era: "bh", eraYear: 1, monthCode: "M07", day: 17 }, 0],
[{ era: "ah", eraYear: 1, monthCode: "M11", day: 25 }, 1],
[{ era: "ah", eraYear: 1447, monthCode: "M05", day: 24 }, 1447],
],
japanese: [
[{ era: "bce", eraYear: 2, monthCode: "M06", day: 14 }, -1],
[{ era: "bce", eraYear: 1, monthCode: "M12", day: 3 }, 0],
[{ era: "ce", eraYear: 1, monthCode: "M07", day: 26 }, 1],
[{ era: "meiji", eraYear: 1, monthCode: "M12", day: 31 }, 1868],
[{ era: "taisho", eraYear: 1, monthCode: "M12", day: 31 }, 1912],
[{ era: "showa", eraYear: 1, monthCode: "M12", day: 31 }, 1926],
[{ era: "heisei", eraYear: 1, monthCode: "M12", day: 31 }, 1989],
[{ era: "reiwa", eraYear: 1, monthCode: "M12", day: 31 }, 2019],
[{ era: "reiwa", eraYear: 7, monthCode: "M11", day: 15 }, 2025],
],
// One era; arithmetic year equals era year
persian: [
[{ era: "ap", eraYear: -1, monthCode: "M09", day: 25 }, -1],
[{ era: "ap", eraYear: 1, monthCode: "M08", day: 22 }, 1],
[{ era: "ap", eraYear: 1404, monthCode: "M08", day: 24 }, 1404],
],
roc: [
[{ era: "broc", eraYear: 2, monthCode: "M09", day: 25 }, -1],
[{ era: "broc", eraYear: 1, monthCode: "M05", day: 3 }, 0],
[{ era: "roc", eraYear: 1, monthCode: "M12", day: 18 }, 1],
[{ era: "roc", eraYear: 114, monthCode: "M11", day: 15 }, 114],
],
};
for (const [calendar, cases] of Object.entries(tests)) {
for (const [fromArgs, expectedYear] of cases) {
const date = Temporal.ZonedDateTime.from({ ...fromArgs, hour: 12, minute: 24, timeZone: "UTC", calendar }, { overflow: "reject" });
assert.sameValue(date.year, expectedYear, `${date} should have arithmetic year ${expectedYear}`);
}
}

View File

@ -0,0 +1,54 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.year
description: >
Determination of the epoch year for arithmetic years for all non-ISO8601
calendars
info: |
4.1.12 CalendarDateArithmeticYear ( _calendar_, _date_ )
1. Let _r_ be the row in Table 4 which the value of the Calendar column is
_calendar_.
2. Let _epochYear_ be the value given in the "Epoch ISO Year" column of _r_.
3. Let _epochDate_ be the first day of the calendar year starting in ISO year
epochYear in the calendar represented by _calendar_, according to
implementation-defined processing.
4. Let _newYear_ be the first day of the calendar year of _date_ in the
calendar represented by _calendar_, according to implementation-defined
processing.
5. Let _arithmeticYear_ be the number of whole years between _epochDate_ and
_newYear_ in the calendar represented by _calendar_, according to
implementation-defined processing.
6. Return _arithmeticYear_.
features: [Temporal, Intl.Era-monthcode]
---*/
const epochYears = {
buddhist: -543,
// Chinese calendar omitted, in order to avoid creating an instance outside
// the well-defined range
coptic: 283,
// Dangi calendar omitted, see above
ethioaa: -5493,
ethiopic: 7,
gregory: 0,
hebrew: -3761,
indian: 78,
"islamic-civil": 621,
"islamic-tbla": 621,
"islamic-umalqura": 621,
japanese: 0,
persian: 621,
roc: 1911,
};
for (const [calendar, epochYear] of Object.entries(epochYears)) {
const epochDate = new Temporal.PlainDate(epochYear, 12, 31)
.withCalendar(calendar)
.with({ monthCode: "M01", day: 1 })
.toZonedDateTime("UTC")
assert.sameValue(epochDate.year, 0, `${calendar} arithmetic year 0 should start in ISO year ${epochYear}`);
}