From 169f1c443fa456b2b25159a1b2a6bf058c349c7c Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Tue, 8 Apr 2025 02:09:35 +0200 Subject: [PATCH 1/4] ICU-23101 Add extended year test for Chinese, Gregorian, Japanese, ... --- icu4c/source/test/intltest/caltest.cpp | 36 ++++++++++++++++++++++++++ icu4c/source/test/intltest/caltest.h | 2 ++ 2 files changed, 38 insertions(+) diff --git a/icu4c/source/test/intltest/caltest.cpp b/icu4c/source/test/intltest/caltest.cpp index 124ccc5971d7..9642180a57e8 100644 --- a/icu4c/source/test/intltest/caltest.cpp +++ b/icu4c/source/test/intltest/caltest.cpp @@ -216,6 +216,7 @@ void CalendarTest::runIndexedTest( int32_t index, UBool exec, const char* &name, TESTCASE_AUTO(TestAddOverflow); TESTCASE_AUTO(Test22750Roll); + TESTCASE_AUTO(Test23101ExtendedYear); TESTCASE_AUTO(TestChineseCalendarComputeMonthStart); TESTCASE_AUTO(Test22962MonthAddOneOverflow); @@ -6148,6 +6149,41 @@ void CalendarTest::Test22750Roll() { } } +void CalendarTest::Test23101ExtendedYear() { + IcuTestErrorCode status(*this, "Test23101ExtendedYear"); + + static const struct TestCase { + const char* calendarName; + const char16_t* expectedExtendedYear20250101; + const char16_t* expectedExtendedYear20250701; + } testCases[] = { + { "gregory", u"2025", u"2025" }, + { "chinese", u"4661", u"4662" }, + { "japanese", u"2025", u"2025" }, + { "ethiopic", u"2017", u"2017" }, + { "ethiopic-amete-alem", u"2017", u"2017" }, + }; + + const UDate date20250101 = (UDate) 1735689600000; + const UDate date20250701 = (UDate) 1751328000000; + + for (const TestCase& testCase : testCases) { + Locale locale; + locale.setKeywordValue("calendar", testCase.calendarName, status); + if (status.errIfFailureAndReset()) { return; } + LocalPointer cal(Calendar::createInstance( + *TimeZone::getGMT(), locale, status)); + SimpleDateFormat formatter(u"u", Locale::getRoot(), status); + if (status.errIfFailureAndReset()) { return; } + formatter.setCalendar(*cal); + UnicodeString actual20250101, actual20250701; + formatter.format(date20250101, actual20250101); + formatter.format(date20250701, actual20250701); + assertEquals("2025-01-01", testCase.expectedExtendedYear20250101, actual20250101); + assertEquals("2025-07-01", testCase.expectedExtendedYear20250701, actual20250701); + } +} + #endif /* #if !UCONFIG_NO_FORMATTING */ //eof diff --git a/icu4c/source/test/intltest/caltest.h b/icu4c/source/test/intltest/caltest.h index 7d4b70d93cb2..3a58a82fb1b6 100644 --- a/icu4c/source/test/intltest/caltest.h +++ b/icu4c/source/test/intltest/caltest.h @@ -356,6 +356,8 @@ class CalendarTest: public CalendarTimeZoneTest { void Test22750Roll(); + void Test23101ExtendedYear(); + void RunTestOnCalendars(void(TestFunc)(Calendar*, UCalendarDateFields)); void verifyFirstDayOfWeek(const char* locale, UCalendarDaysOfWeek expected); From f1851b0ad52ba07ae5c8fe85cde4c7f6011d5684 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Mon, 21 Jul 2025 18:13:54 -0700 Subject: [PATCH 2/4] Add Dangi --- icu4c/source/test/intltest/caltest.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/icu4c/source/test/intltest/caltest.cpp b/icu4c/source/test/intltest/caltest.cpp index 9642180a57e8..66087b84d92d 100644 --- a/icu4c/source/test/intltest/caltest.cpp +++ b/icu4c/source/test/intltest/caltest.cpp @@ -6159,6 +6159,7 @@ void CalendarTest::Test23101ExtendedYear() { } testCases[] = { { "gregory", u"2025", u"2025" }, { "chinese", u"4661", u"4662" }, + { "dangi", u"4357", u"4358" }, { "japanese", u"2025", u"2025" }, { "ethiopic", u"2017", u"2017" }, { "ethiopic-amete-alem", u"2017", u"2017" }, From 9c7b576feb9d29f328bebd901a539ee6261ed97b Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Thu, 14 Aug 2025 19:14:01 -0700 Subject: [PATCH 3/4] Port test to Java --- .../test/calendar/CalendarRegressionTest.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/calendar/CalendarRegressionTest.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/calendar/CalendarRegressionTest.java index 72ba574f6e5c..3ca8dd01cedc 100644 --- a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/calendar/CalendarRegressionTest.java +++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/calendar/CalendarRegressionTest.java @@ -2746,5 +2746,34 @@ public void TestMaxActualLimitsWithoutGet23006() { 30, actualMaximumBeforeCallingGet); } + + @Test + public void Test23101ExtendedYear() { + String[][] testCases = { + { "gregory", "2025", "2025" }, + { "chinese", "4661", "4662" }, + { "japanese", "2025", "2025" }, + { "ethiopic", "2017", "2017" }, + { "ethiopic-amete-alem", "2017", "2017" }, + }; + + long date20250101 = 1735689600000l; + long date20250701 = 1751328000000l; + + for (String[] testCase : testCases) { + String calendarName = testCase[0]; + String expectedExtendedYear20250101 = testCase[1]; + String expectedExtendedYear20250701 = testCase[2]; + + ULocale locale = ULocale.forLanguageTag("und-u-ca-" + calendarName); + Calendar cal = Calendar.getInstance(TimeZone.GMT_ZONE, locale); + SimpleDateFormat formatter = new SimpleDateFormat("u", ULocale.ROOT); + formatter.setCalendar(cal); + String actual20250101 = formatter.format(date20250101); + String actual20250701 = formatter.format(date20250701); + assertEquals("2025-01-01 in " + calendarName, expectedExtendedYear20250101, actual20250101); + assertEquals("2025-07-01 in " + calendarName, expectedExtendedYear20250701, actual20250701); + } + } } //eof From bf70eb47c091f0ba5e82eabc37c6d8e537dfeecf Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Thu, 14 Aug 2025 19:35:23 -0700 Subject: [PATCH 4/4] Change Chinese behavior in Java --- .../java/com/ibm/icu/util/ChineseCalendar.java | 2 +- .../dev/test/calendar/CalendarRegressionTest.java | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/icu4j/main/core/src/main/java/com/ibm/icu/util/ChineseCalendar.java b/icu4j/main/core/src/main/java/com/ibm/icu/util/ChineseCalendar.java index d4ba973018f5..12a00871600a 100644 --- a/icu4j/main/core/src/main/java/com/ibm/icu/util/ChineseCalendar.java +++ b/icu4j/main/core/src/main/java/com/ibm/icu/util/ChineseCalendar.java @@ -829,7 +829,7 @@ protected void handleComputeFields(int julianDay) { MonthInfo info = computeMonthInfo(days, gyear); // Extended year and cycle year is based on the epoch year - int extended_year = gyear - epochYear; + int extended_year = gyear - 1; int cycle_year = gyear - CHINESE_EPOCH_YEAR; if (info.month < 10 || gmonth >= JULY) { diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/calendar/CalendarRegressionTest.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/calendar/CalendarRegressionTest.java index 3ca8dd01cedc..1e2576a3fbae 100644 --- a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/calendar/CalendarRegressionTest.java +++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/calendar/CalendarRegressionTest.java @@ -2750,20 +2750,23 @@ public void TestMaxActualLimitsWithoutGet23006() { @Test public void Test23101ExtendedYear() { String[][] testCases = { - { "gregory", "2025", "2025" }, - { "chinese", "4661", "4662" }, - { "japanese", "2025", "2025" }, - { "ethiopic", "2017", "2017" }, - { "ethiopic-amete-alem", "2017", "2017" }, + { "gregory", "2025", "2025", "2025" }, + { "chinese", "2024", "2025", "2025" }, + { "dangi", "2024", "2025", "2025" }, + { "japanese", "2025", "2025", "2025" }, + { "ethiopic", "2017", "2017", "2018" }, + { "ethiopic-amete-alem", "2017", "2017", "2018" }, }; long date20250101 = 1735689600000l; long date20250701 = 1751328000000l; + long date20251231 = 1767139200000l; for (String[] testCase : testCases) { String calendarName = testCase[0]; String expectedExtendedYear20250101 = testCase[1]; String expectedExtendedYear20250701 = testCase[2]; + String expectedExtendedYear20251231 = testCase[3]; ULocale locale = ULocale.forLanguageTag("und-u-ca-" + calendarName); Calendar cal = Calendar.getInstance(TimeZone.GMT_ZONE, locale); @@ -2771,8 +2774,10 @@ public void Test23101ExtendedYear() { formatter.setCalendar(cal); String actual20250101 = formatter.format(date20250101); String actual20250701 = formatter.format(date20250701); + String actual20251231 = formatter.format(date20251231); assertEquals("2025-01-01 in " + calendarName, expectedExtendedYear20250101, actual20250101); assertEquals("2025-07-01 in " + calendarName, expectedExtendedYear20250701, actual20250701); + assertEquals("2025-12-31 in " + calendarName, expectedExtendedYear20251231, actual20251231); } } }