]> granicus.if.org Git - icu/commitdiff
ICU-12504 in ICU4C Persian cal, use int64_t math for one operation to avoid overflow...
authorPeter Edberg <pedberg@unicode.org>
Sun, 29 Oct 2017 03:38:05 +0000 (03:38 +0000)
committerPeter Edberg <pedberg@unicode.org>
Sun, 29 Oct 2017 03:38:05 +0000 (03:38 +0000)
X-SVN-Rev: 40654

icu4c/source/i18n/gregoimp.cpp
icu4c/source/i18n/gregoimp.h
icu4c/source/i18n/persncal.cpp
icu4c/source/test/intltest/calregts.cpp
icu4c/source/test/intltest/calregts.h
icu4j/main/tests/core/src/com/ibm/icu/dev/test/calendar/CalendarRegressionTest.java

index e62044b361a5620cdd2db04f6a78676b296b5e98..537aa19d8a40ed44a3eb9b5aaf8075ae0eb11be0 100644 (file)
@@ -27,6 +27,11 @@ int32_t ClockMath::floorDivide(int32_t numerator, int32_t denominator) {
         numerator / denominator : ((numerator + 1) / denominator) - 1;
 }
 
+int64_t ClockMath::floorDivide(int64_t numerator, int64_t denominator) {
+    return (numerator >= 0) ?
+        numerator / denominator : ((numerator + 1) / denominator) - 1;
+}
+
 int32_t ClockMath::floorDivide(double numerator, int32_t denominator,
                           int32_t& remainder) {
     double quotient;
index b30741679df2f575d17e01269dab3a40e6b5745e..afaacda0b41a74426ac0acb18ab8a018f54eea9d 100644 (file)
@@ -40,6 +40,17 @@ class ClockMath {
      */
     static int32_t floorDivide(int32_t numerator, int32_t denominator);
 
+    /**
+     * Divide two integers, returning the floor of the quotient.
+     * Unlike the built-in division, this is mathematically
+     * well-behaved.  E.g., <code>-1/4</code> => 0 but
+     * <code>floorDivide(-1,4)</code> => -1.
+     * @param numerator the numerator
+     * @param denominator a divisor which must be != 0
+     * @return the floor of the quotient
+     */
+    static int64_t floorDivide(int64_t numerator, int64_t denominator);
+
     /**
      * Divide two numbers, returning the floor of the quotient.
      * Unlike the built-in division, this is mathematically
index f66ac676a4a7e1fc44f71cc35d3f2ea4eb857ff7..f38f779fea9f5d6afeed633d79b29a0348344f5d 100644 (file)
@@ -213,7 +213,7 @@ void PersianCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*statu
     int32_t year, month, dayOfMonth, dayOfYear;
 
     int32_t daysSinceEpoch = julianDay - PERSIAN_EPOCH;
-    year = 1 + ClockMath::floorDivide(33 * daysSinceEpoch + 3, 12053);
+    year = 1 + (int32_t)ClockMath::floorDivide(33 * (int64_t)daysSinceEpoch + 3, (int64_t)12053);
 
     int32_t farvardin1 = 365 * (year - 1) + ClockMath::floorDivide(8 * year + 21, 33);
     dayOfYear = (daysSinceEpoch - farvardin1); // 0-based
index 17fc81425c4305a882141a57814601acc253a404..f1eb17bbed327e5216893c24d26c796695d4e04f 100644 (file)
@@ -12,6 +12,7 @@
 
 #include "calregts.h"
 
+#include "unicode/calendar.h"
 #include "unicode/gregocal.h"
 #include "unicode/simpletz.h"
 #include "unicode/smpdtfmt.h"
@@ -91,6 +92,7 @@ CalendarRegressionTest::runIndexedTest( int32_t index, UBool exec, const char* &
         CASE(49,Test9019);
         CASE(50,TestT9452);
         CASE(51,TestT11632);
+        CASE(52,TestPersianCalOverflow);
     default: name = ""; break;
     }
 }
@@ -2985,6 +2987,36 @@ void CalendarRegressionTest::TestT11632(void) {
             assertEquals("correct datetime displayed for hour value", UnicodeString("1970-01-13T12:00:00"), dstr);
         }
     }
-} 
+}
+
+/**
+ * @bug ticket 13454
+ */
+void CalendarRegressionTest::TestPersianCalOverflow(void) {
+    const char* localeID = "bs_Cyrl@calendar=persian";
+    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, month, dayOfMonth;
+        for (jd = 67023580; jd <= 67023584; jd++) { // year 178171, int32_t overflow if jd >= 67023582
+            status = U_ZERO_ERROR;
+            cal->clear();
+            cal->set(UCAL_JULIAN_DAY, jd);
+            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)); 
+            } else if (month > maxMonth || dayOfMonth > maxDayOfMonth) {
+                errln("FAIL: localeID %s, julianDay %d; maxMonth %d, got month %d; maxDayOfMonth %d, got dayOfMonth %d\n",
+                        localeID, jd, maxMonth, month, maxDayOfMonth, dayOfMonth); 
+            }
+        }
+        delete cal;
+    }
+}
 
 #endif /* #if !UCONFIG_NO_FORMATTING */
index e576b972631d8adffe0d02dfd277418535c940fd..15d550290935ee61b75070b2b41cdef4261adf81 100644 (file)
@@ -78,6 +78,7 @@ public:
     void Test9019(void);
     void TestT9452(void);
     void TestT11632(void);
+    void TestPersianCalOverflow(void);
 
     void printdate(GregorianCalendar *cal, const char *string);
     void dowTest(UBool lenient) ;
index e0ca7f7ffddca13e565291813bc4e98013c2a937..062ac0bd7a44df16c218191d2442cf87a9d3ee16 100644 (file)
@@ -2503,5 +2503,23 @@ public class CalendarRegressionTest extends com.ibm.icu.dev.test.TestFmwk {
         assertEquals("Incorrect time for long range milliseconds","Sun Jan 25 21:00:00 PST 1970", cal.getTime().toString());
     }
 
-}
+    @Test
+    public void TestPersianCalOverflow() {
+        String localeID = "bs_Cyrl@calendar=persian";
+        Calendar cal = Calendar.getInstance(new ULocale(localeID));
+        int maxMonth = cal.getMaximum(Calendar.MONTH);
+        int maxDayOfMonth = cal.getMaximum(Calendar.DATE);
+        int jd, month, dayOfMonth;
+        for (jd = 67023580; jd <= 67023584; jd++) { // year 178171, int32_t overflow if jd >= 67023582
+            cal.clear();
+            cal.set(Calendar.JULIAN_DAY, jd);
+            month = cal.get(Calendar.MONTH);
+            dayOfMonth = cal.get(Calendar.DATE);
+            if (month > maxMonth || dayOfMonth > maxDayOfMonth) {
+                errln("Error: localeID " + localeID + ", julianDay " + jd + "; maxMonth " + maxMonth + ", got month " + month +
+                        "; maxDayOfMonth " + maxDayOfMonth + ", got dayOfMonth " + dayOfMonth);
+            }
+        }
+    }
+ }
 //eof