From 64aa4beb28a435dfc9c17347a460c141a82c3859 Mon Sep 17 00:00:00 2001 From: Peter Edberg Date: Fri, 9 Feb 2018 19:31:12 +0000 Subject: [PATCH] ICU-13538 in ICU4C Islamic cal, use int64_t math for one operation to avoid overflow; add tests in C and J X-SVN-Rev: 40882 --- icu4c/source/i18n/islamcal.cpp | 2 +- icu4c/source/test/intltest/calregts.cpp | 36 +++++++++++++++++-- icu4c/source/test/intltest/calregts.h | 1 + .../test/calendar/CalendarRegressionTest.java | 22 +++++++++++- 4 files changed, 57 insertions(+), 4 deletions(-) diff --git a/icu4c/source/i18n/islamcal.cpp b/icu4c/source/i18n/islamcal.cpp index 4fd0e07d920..b84bedfa091 100644 --- a/icu4c/source/i18n/islamcal.cpp +++ b/icu4c/source/i18n/islamcal.cpp @@ -614,7 +614,7 @@ void IslamicCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) days = julianDay - ASTRONOMICAL_EPOC; } // Use the civil calendar approximation, which is just arithmetic - year = (int)ClockMath::floorDivide( (double)(30 * days + 10646) , 10631.0 ); + year = (int32_t)ClockMath::floorDivide(30 * (int64_t)days + 10646, (int64_t)10631); month = (int32_t)uprv_ceil((days - 29 - yearStart(year)) / 29.5 ); month = month<11?month:11; startDate = monthStart(year, month); diff --git a/icu4c/source/test/intltest/calregts.cpp b/icu4c/source/test/intltest/calregts.cpp index f1eb17bbed3..24951e5b8aa 100644 --- a/icu4c/source/test/intltest/calregts.cpp +++ b/icu4c/source/test/intltest/calregts.cpp @@ -93,6 +93,7 @@ CalendarRegressionTest::runIndexedTest( int32_t index, UBool exec, const char* & CASE(50,TestT9452); CASE(51,TestT11632); CASE(52,TestPersianCalOverflow); + CASE(53,TestIslamicCalOverflow); default: name = ""; break; } } @@ -3009,9 +3010,9 @@ void CalendarRegressionTest::TestPersianCalOverflow(void) { month = cal->get(UCAL_MONTH, status); dayOfMonth = cal->get(UCAL_DATE, status); if ( U_FAILURE(status) ) { - errln("FAIL: Calendar->get MONTH/DATE for localeID %s, julianDay %d, status %s\n", localeID, jd, u_errorName(status)); + errln("FAIL: Calendar->get MONTH/DATE for localeID %s, julianDay %d, status %s", localeID, jd, u_errorName(status)); } else if (month > maxMonth || dayOfMonth > maxDayOfMonth) { - errln("FAIL: localeID %s, julianDay %d; maxMonth %d, got month %d; maxDayOfMonth %d, got dayOfMonth %d\n", + errln("FAIL: localeID %s, julianDay %d; maxMonth %d, got month %d; maxDayOfMonth %d, got dayOfMonth %d", localeID, jd, maxMonth, month, maxDayOfMonth, dayOfMonth); } } @@ -3019,4 +3020,35 @@ void CalendarRegressionTest::TestPersianCalOverflow(void) { } } +/** + * @bug tickets 12661, 13538 + */ +void CalendarRegressionTest::TestIslamicCalOverflow(void) { + const char* localeID = "ar@calendar=islamic-civil"; + UErrorCode status = U_ZERO_ERROR; + Calendar* cal = Calendar::createInstance(Locale(localeID), status); + if(U_FAILURE(status)) { + dataerrln("FAIL: Calendar::createInstance for localeID %s: %s", localeID, u_errorName(status)); + } else { + int32_t maxMonth = cal->getMaximum(UCAL_MONTH); + int32_t maxDayOfMonth = cal->getMaximum(UCAL_DATE); + int32_t jd, year, month, dayOfMonth; + for (jd = 73530872; jd <= 73530876; jd++) { // year 202002, int32_t overflow if jd >= 73530874 + status = U_ZERO_ERROR; + cal->clear(); + cal->set(UCAL_JULIAN_DAY, jd); + year = cal->get(UCAL_YEAR, status); + month = cal->get(UCAL_MONTH, status); + dayOfMonth = cal->get(UCAL_DATE, status); + if ( U_FAILURE(status) ) { + errln("FAIL: Calendar->get YEAR/MONTH/DATE for localeID %s, julianDay %d, status %s", localeID, jd, u_errorName(status)); + } else if (month > maxMonth || dayOfMonth > maxDayOfMonth) { + errln("FAIL: localeID %s, julianDay %d; got year %d; maxMonth %d, got month %d; maxDayOfMonth %d, got dayOfMonth %d", + localeID, jd, year, maxMonth, month, maxDayOfMonth, dayOfMonth); + } + } + delete cal; + } +} + #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/test/intltest/calregts.h b/icu4c/source/test/intltest/calregts.h index 15d55029093..7d36fab0b45 100644 --- a/icu4c/source/test/intltest/calregts.h +++ b/icu4c/source/test/intltest/calregts.h @@ -79,6 +79,7 @@ public: void TestT9452(void); void TestT11632(void); void TestPersianCalOverflow(void); + void TestIslamicCalOverflow(void); void printdate(GregorianCalendar *cal, const char *string); void dowTest(UBool lenient) ; diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/calendar/CalendarRegressionTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/calendar/CalendarRegressionTest.java index 062ac0bd7a4..4371b672039 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/calendar/CalendarRegressionTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/calendar/CalendarRegressionTest.java @@ -2521,5 +2521,25 @@ public class CalendarRegressionTest extends com.ibm.icu.dev.test.TestFmwk { } } } - } + + @Test + public void TestIslamicCalOverflow() { + String localeID = "ar@calendar=islamic-civil"; + Calendar cal = Calendar.getInstance(new ULocale(localeID)); + int maxMonth = cal.getMaximum(Calendar.MONTH); + int maxDayOfMonth = cal.getMaximum(Calendar.DATE); + int jd, year, month, dayOfMonth; + for (jd = 73530872; jd <= 73530876; jd++) { // year 202002, int32_t overflow if jd >= 73530874 + cal.clear(); + cal.set(Calendar.JULIAN_DAY, jd); + year = cal.get(Calendar.YEAR); + month = cal.get(Calendar.MONTH); + dayOfMonth = cal.get(Calendar.DATE); + if (month > maxMonth || dayOfMonth > maxDayOfMonth) { + errln("Error: localeID " + localeID + ", julianDay " + jd + "; got year " + year + "; maxMonth " + maxMonth + + ", got month " + month + "; maxDayOfMonth " + maxDayOfMonth + ", got dayOfMonth " + dayOfMonth); + } + } + } +} //eof -- 2.40.0