Skip to content

Commit 76cb092

Browse files
jessealamaptomato
authored andcommitted
Add PlainMonthDay month code tests for non-ISO calendars
1 parent 26c0889 commit 76cb092

32 files changed

+1271
-0
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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.plainmonthday.from
6+
description: M13 month code is invalid for Buddhist calendar (12-month calendar)
7+
features: [Temporal, Intl.Era-monthcode]
8+
---*/
9+
10+
// The Buddhist calendar is a 12-month calendar and should not accept M13
11+
12+
const calendar = "buddhist";
13+
14+
assert.throws(RangeError, () => {
15+
Temporal.PlainMonthDay.from({ calendar, monthCode: "M13", day: 1 });
16+
}, `M13 should not be a valid month code for ${calendar} calendar`);
17+
18+
// M13 should throw even with overflow: "constrain"
19+
assert.throws(RangeError, () => {
20+
Temporal.PlainMonthDay.from({ calendar, monthCode: "M13", day: 1 }, { overflow: "constrain" });
21+
}, `M13 should not be valid for ${calendar} calendar even with constrain overflow`);
22+
23+
// M13 should throw with overflow: "reject"
24+
assert.throws(RangeError, () => {
25+
Temporal.PlainMonthDay.from({ calendar, monthCode: "M13", day: 1 }, { overflow: "reject" });
26+
}, `M13 should not be valid for ${calendar} calendar with reject overflow`);
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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.plainmonthday.from
6+
description: PlainMonthDay can be created for all month codes (M01-M12) in Buddhist calendar
7+
features: [Temporal, Intl.Era-monthcode]
8+
includes: [temporalHelpers.js]
9+
---*/
10+
11+
// Test that all month codes M01-M12 are valid for the Buddhist calendar
12+
// The Buddhist calendar follows the Gregorian calendar structure
13+
14+
const calendar = "buddhist";
15+
16+
for (const { monthCode, daysInMonth } of TemporalHelpers.ISOMonths) {
17+
// Test creation with monthCode and day 1
18+
const pmd = Temporal.PlainMonthDay.from({ calendar, monthCode, day: 1 });
19+
TemporalHelpers.assertPlainMonthDay(pmd, monthCode, 1, `monthCode ${monthCode} should be preserved`);
20+
21+
// Test with maximum day value for this month (minimum for PlainMonthDay)
22+
const pmdMax = Temporal.PlainMonthDay.from({ calendar, monthCode, day: daysInMonth });
23+
TemporalHelpers.assertPlainMonthDay(pmdMax, monthCode, daysInMonth, `${monthCode} with day ${daysInMonth} should be valid`);
24+
25+
// Test constrain overflow
26+
const constrained = Temporal.PlainMonthDay.from(
27+
{ calendar, monthCode, day: daysInMonth + 1 },
28+
{ overflow: "constrain" }
29+
);
30+
TemporalHelpers.assertPlainMonthDay(constrained, monthCode, daysInMonth, `day ${daysInMonth + 1} should be constrained to ${daysInMonth} for ${monthCode}`);
31+
32+
// Test reject overflow
33+
assert.throws(RangeError, () => {
34+
Temporal.PlainMonthDay.from({ calendar, monthCode, day: daysInMonth + 1 }, { overflow: "reject" });
35+
}, `${monthCode} with day ${daysInMonth + 1} should throw with reject overflow`);
36+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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.plainmonthday.from
6+
description: M13 month code is invalid for Chinese calendar (12-month calendar with leap months)
7+
features: [Temporal, Intl.Era-monthcode]
8+
---*/
9+
10+
// The Chinese calendar is a 12-month lunisolar calendar with leap months (M01L-M12L)
11+
// but does not have a thirteenth month (M13)
12+
13+
const calendar = "chinese";
14+
15+
assert.throws(RangeError, () => {
16+
Temporal.PlainMonthDay.from({ calendar, monthCode: "M13", day: 1 });
17+
}, `M13 should not be a valid month code for ${calendar} calendar`);
18+
19+
// M13 should throw even with overflow: "constrain"
20+
assert.throws(RangeError, () => {
21+
Temporal.PlainMonthDay.from({ calendar, monthCode: "M13", day: 1 }, { overflow: "constrain" });
22+
}, `M13 should not be valid for ${calendar} calendar even with constrain overflow`);
23+
24+
// M13 should throw with overflow: "reject"
25+
assert.throws(RangeError, () => {
26+
Temporal.PlainMonthDay.from({ calendar, monthCode: "M13", day: 1 }, { overflow: "reject" });
27+
}, `M13 should not be valid for ${calendar} calendar with reject overflow`);
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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.plainmonthday.from
6+
description: PlainMonthDay can be created for common leap month codes in Chinese calendar
7+
features: [Temporal, Intl.Era-monthcode]
8+
---*/
9+
10+
// Test common leap months in the Chinese calendar
11+
// The Chinese calendar is a lunisolar calendar where leap months are inserted
12+
// to keep the lunar year synchronized with the solar year.
13+
// Common leap months (occurring more frequently in the astronomical cycle):
14+
// M02L, M03L, M04L, M05L, M06L, M07L, M08L
15+
//
16+
// The distribution of leap months follows astronomical calculations.
17+
//
18+
// Note: M01L, M02L and M08L through M12L are temporarily omitted from this
19+
// test because while any leap month can have 30 days, there isn't a year in the
20+
// supported range where these months do, and it's not yet well-defined what
21+
// reference ISO year should be used.
22+
// See https://github.com/tc39/proposal-intl-era-monthcode/issues/60
23+
24+
const calendar = "chinese";
25+
26+
// Test leap months M03L-M07L with day 30
27+
// These are well-established leap months that can have 30 days
28+
const leapMonthsWith30Days = ["M03L", "M04L", "M05L", "M06L", "M07L"];
29+
30+
for (const monthCode of leapMonthsWith30Days) {
31+
// Test creation with monthCode
32+
const pmd = Temporal.PlainMonthDay.from({ calendar, monthCode, day: 1 });
33+
assert.sameValue(pmd.monthCode, monthCode, `leap monthCode ${monthCode} should be preserved`);
34+
assert.sameValue(pmd.day, 1, `day should be 1 for ${monthCode}`);
35+
36+
// These leap months can have up to 30 days (minimum for PlainMonthDay)
37+
const pmd30 = Temporal.PlainMonthDay.from({ calendar, monthCode, day: 30 });
38+
assert.sameValue(pmd30.monthCode, monthCode, `${monthCode} with day 30 should be valid`);
39+
assert.sameValue(pmd30.day, 30, `day should be 30 for ${monthCode}`);
40+
41+
// Test constrain overflow - day 31 should be constrained to 30
42+
const constrained = Temporal.PlainMonthDay.from(
43+
{ calendar, monthCode, day: 31 },
44+
{ overflow: "constrain" }
45+
);
46+
assert.sameValue(constrained.monthCode, monthCode, `${monthCode} should be preserved with constrain`);
47+
assert.sameValue(constrained.day, 30, `day 31 should be constrained to 30 for ${monthCode}`);
48+
49+
// Test reject overflow - day 31 should throw
50+
assert.throws(RangeError, () => {
51+
Temporal.PlainMonthDay.from({ calendar, monthCode, day: 31 }, { overflow: "reject" });
52+
}, `${monthCode} with day 31 should throw with reject overflow`);
53+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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.plainmonthday.from
6+
description: PlainMonthDay can be created for all regular month codes (M01-M12) in Chinese calendar
7+
features: [Temporal, Intl.Era-monthcode]
8+
---*/
9+
10+
// Test that all regular month codes M01-M12 are valid for the Chinese calendar
11+
// The Chinese calendar is a lunisolar calendar, so months vary in length
12+
// Leap months (M01L-M12L) are tested elsewhere
13+
14+
const calendar = "chinese";
15+
const monthCodes = [
16+
"M01", "M02", "M03", "M04", "M05", "M06",
17+
"M07", "M08", "M09", "M10", "M11", "M12"
18+
];
19+
20+
for (const monthCode of monthCodes) {
21+
// Test creation with monthCode
22+
const pmd = Temporal.PlainMonthDay.from({ calendar, monthCode, day: 1 });
23+
assert.sameValue(pmd.monthCode, monthCode, `monthCode ${monthCode} should be preserved`);
24+
assert.sameValue(pmd.day, 1, "day should be 1");
25+
26+
// Test with day 30 (months can have 29 or 30 days)
27+
const pmd30 = Temporal.PlainMonthDay.from({ calendar, monthCode, day: 30 });
28+
assert.sameValue(pmd30.monthCode, monthCode, `${monthCode} with day 30 should be valid`);
29+
assert.sameValue(pmd30.day, 30, `day should be 30 for ${monthCode}`);
30+
31+
// Test constrain overflow - Chinese months vary from 29-30 days
32+
const constrained = Temporal.PlainMonthDay.from(
33+
{ calendar, monthCode, day: 31 },
34+
{ overflow: "constrain" }
35+
);
36+
assert.sameValue(constrained.monthCode, monthCode, `${monthCode} should be preserved with constrain`);
37+
assert.sameValue(constrained.day, 30, `day 31 should be constrained to 30 for ${monthCode}`);
38+
39+
// Test reject overflow for day 31
40+
assert.throws(RangeError, () => {
41+
Temporal.PlainMonthDay.from({ calendar, monthCode, day: 31 }, { overflow: "reject" });
42+
}, `${monthCode} with day 31 should throw with reject overflow`);
43+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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.plainmonthday.from
6+
description: M14 month code is invalid for Coptic calendar (13-month calendar)
7+
features: [Temporal, Intl.Era-monthcode]
8+
---*/
9+
10+
// The Coptic calendar has 13 months (M01-M13) and should not accept M14
11+
12+
const calendar = "coptic";
13+
14+
assert.throws(RangeError, () => {
15+
Temporal.PlainMonthDay.from({ calendar, monthCode: "M14", day: 1 });
16+
}, `M14 should not be a valid month code for ${calendar} calendar`);
17+
18+
// M14 should throw even with overflow: "constrain"
19+
assert.throws(RangeError, () => {
20+
Temporal.PlainMonthDay.from({ calendar, monthCode: "M14", day: 1 }, { overflow: "constrain" });
21+
}, `M14 should not be valid for ${calendar} calendar even with constrain overflow`);
22+
23+
// M14 should throw with overflow: "reject"
24+
assert.throws(RangeError, () => {
25+
Temporal.PlainMonthDay.from({ calendar, monthCode: "M14", day: 1 }, { overflow: "reject" });
26+
}, `M14 should not be valid for ${calendar} calendar with reject overflow`);
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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.plainmonthday.from
6+
description: PlainMonthDay can be created for all month codes (M01-M13) in Coptic calendar
7+
features: [Temporal, Intl.Era-monthcode]
8+
---*/
9+
10+
// Test that all month codes M01-M13 are valid for the Coptic calendar
11+
// The Coptic calendar has 12 months of 30 days each, plus a 13th month (Epagomenal days)
12+
// of 5 or 6 days
13+
14+
const calendar = "coptic";
15+
16+
// M01-M12: Regular months with 30 days each
17+
const regularMonthCodes = [
18+
"M01", "M02", "M03", "M04", "M05", "M06",
19+
"M07", "M08", "M09", "M10", "M11", "M12"
20+
];
21+
22+
for (const monthCode of regularMonthCodes) {
23+
// Test creation with monthCode
24+
const pmd = Temporal.PlainMonthDay.from({ calendar, monthCode, day: 1 });
25+
assert.sameValue(pmd.monthCode, monthCode, `monthCode ${monthCode} should be preserved`);
26+
assert.sameValue(pmd.day, 1, "day should be 1");
27+
28+
// Test with day 30 (all regular months have 30 days)
29+
const pmd30 = Temporal.PlainMonthDay.from({ calendar, monthCode, day: 30 });
30+
assert.sameValue(pmd30.monthCode, monthCode, `${monthCode} with day 30 should be valid`);
31+
assert.sameValue(pmd30.day, 30, `day should be 30 for ${monthCode}`);
32+
33+
// Test overflow: constrain to 30
34+
const constrained = Temporal.PlainMonthDay.from(
35+
{ calendar, monthCode, day: 31 },
36+
{ overflow: "constrain" }
37+
);
38+
assert.sameValue(constrained.monthCode, monthCode, `${monthCode} should be preserved with constrain`);
39+
assert.sameValue(constrained.day, 30, `day 31 should be constrained to 30 for ${monthCode}`);
40+
41+
// Test overflow: reject should throw for day 31
42+
assert.throws(RangeError, () => {
43+
Temporal.PlainMonthDay.from({ calendar, monthCode, day: 31 }, { overflow: "reject" });
44+
}, `${monthCode} with day 31 should throw with reject overflow`);
45+
}
46+
47+
// M13: Short month (Epagomenal days) with 5 or 6 days
48+
49+
// Test M13 with day 6 (maximum, valid in leap years)
50+
const pmdM13Day6 = Temporal.PlainMonthDay.from({ calendar, monthCode: "M13", day: 6 });
51+
assert.sameValue(pmdM13Day6.monthCode, "M13", "M13 should be valid with day 6");
52+
assert.sameValue(pmdM13Day6.day, 6, "day should be 6 for M13");
53+
54+
// Test M13 overflow: constrain to maximum
55+
const constrained = Temporal.PlainMonthDay.from(
56+
{ calendar, monthCode: "M13", day: 7 },
57+
{ overflow: "constrain" }
58+
);
59+
assert.sameValue(constrained.monthCode, "M13", "M13 should be preserved with constrain");
60+
assert.sameValue(constrained.day, 6, "day 7 should be constrained to 6 for M13");
61+
62+
// Test M13 overflow: reject should throw for day 7
63+
assert.throws(RangeError, () => {
64+
Temporal.PlainMonthDay.from({ calendar, monthCode: "M13", day: 7 }, { overflow: "reject" });
65+
}, "M13 with day 7 should throw with reject overflow");
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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.plainmonthday.from
6+
description: M13 month code is invalid for Dangi calendar (12-month calendar with leap months)
7+
features: [Temporal, Intl.Era-monthcode]
8+
---*/
9+
10+
// The Dangi calendar is a 12-month lunisolar calendar with leap months (M01L-M12L)
11+
// but does not have a thirteenth month (M13)
12+
13+
const calendar = "dangi";
14+
15+
assert.throws(RangeError, () => {
16+
Temporal.PlainMonthDay.from({ calendar, monthCode: "M13", day: 1 });
17+
}, `M13 should not be a valid month code for ${calendar} calendar`);
18+
19+
// M13 should throw even with overflow: "constrain"
20+
assert.throws(RangeError, () => {
21+
Temporal.PlainMonthDay.from({ calendar, monthCode: "M13", day: 1 }, { overflow: "constrain" });
22+
}, `M13 should not be valid for ${calendar} calendar even with constrain overflow`);
23+
24+
// M13 should throw with overflow: "reject"
25+
assert.throws(RangeError, () => {
26+
Temporal.PlainMonthDay.from({ calendar, monthCode: "M13", day: 1 }, { overflow: "reject" });
27+
}, `M13 should not be valid for ${calendar} calendar with reject overflow`);
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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.plainmonthday.from
6+
description: PlainMonthDay can be created for common leap month codes in Dangi calendar
7+
features: [Temporal, Intl.Era-monthcode]
8+
---*/
9+
10+
// Test common leap months in the Dangi calendar
11+
// Dangi is a lunisolar calendar where leap months follow the same distribution as Chinese
12+
// Common leap months (occurring more frequently in the astronomical cycle):
13+
// M02L, M03L, M04L, M05L, M06L, M07L, M08L
14+
//
15+
// Like the Chinese calendar, the Dangi calendar inserts leap months to keep the
16+
// lunar year synchronized with the solar year. The distribution of leap months
17+
// follows astronomical calculations.
18+
//
19+
// Note: M01L, M02L and M08L through M12L are temporarily omitted from this
20+
// test because while any leap month can have 30 days, there isn't a year in the
21+
// supported range where these months do, and it's not yet well-defined what
22+
// reference ISO year should be used.
23+
// See https://github.com/tc39/proposal-intl-era-monthcode/issues/60
24+
25+
const calendar = "dangi";
26+
27+
// Test leap months M03L-M07L with day 30
28+
// These are well-established leap months that can have 30 days
29+
const leapMonthsWith30Days = ["M03L", "M04L", "M05L", "M06L", "M07L"];
30+
31+
for (const monthCode of leapMonthsWith30Days) {
32+
// Test creation with monthCode
33+
const pmd = Temporal.PlainMonthDay.from({ calendar, monthCode, day: 1 });
34+
assert.sameValue(pmd.monthCode, monthCode, `leap monthCode ${monthCode} should be preserved`);
35+
assert.sameValue(pmd.day, 1, `day should be 1 for ${monthCode}`);
36+
37+
// These leap months can have up to 30 days (minimum for PlainMonthDay)
38+
const pmd30 = Temporal.PlainMonthDay.from({ calendar, monthCode, day: 30 });
39+
assert.sameValue(pmd30.monthCode, monthCode, `${monthCode} with day 30 should be valid`);
40+
assert.sameValue(pmd30.day, 30, `day should be 30 for ${monthCode}`);
41+
42+
// Test constrain overflow - day 31 should be constrained to 30
43+
const constrained = Temporal.PlainMonthDay.from(
44+
{ calendar, monthCode, day: 31 },
45+
{ overflow: "constrain" }
46+
);
47+
assert.sameValue(constrained.monthCode, monthCode, `${monthCode} should be preserved with constrain`);
48+
assert.sameValue(constrained.day, 30, `day 31 should be constrained to 30 for ${monthCode}`);
49+
50+
// Test reject overflow - day 31 should throw
51+
assert.throws(RangeError, () => {
52+
Temporal.PlainMonthDay.from({ calendar, monthCode, day: 31 }, { overflow: "reject" });
53+
}, `${monthCode} with day 31 should throw with reject overflow`);
54+
}

0 commit comments

Comments
 (0)