]> granicus.if.org Git - icu/commitdiff
ICU-9403 Calendar should return error for large negative millis out of range in stric...
authorScott Russell <DTownSMR@gmail.com>
Mon, 8 Oct 2012 20:24:50 +0000 (20:24 +0000)
committerScott Russell <DTownSMR@gmail.com>
Mon, 8 Oct 2012 20:24:50 +0000 (20:24 +0000)
X-SVN-Rev: 32554

icu4c/source/i18n/calendar.cpp
icu4c/source/i18n/gregocal.cpp
icu4c/source/i18n/unicode/calendar.h
icu4c/source/test/cintltst/ccaltst.c

index fa4f83b9bddcf1a92b77a4d98bad74ca751c47e4..656b92f4242d06541b2753550062dcca89efea08 100644 (file)
@@ -1050,6 +1050,9 @@ Calendar::getTimeInMillis(UErrorCode& status) const
 
 /**
 * Sets this Calendar's current time from the given long value.
+* A status of U_ILLEGAL_ARGUMENT_ERROR is set when millis is
+* outside the range permitted by a Calendar object when not in lenient mode.
+* when in lenient mode the out of range values are pinned to their respective min/max.
 * @param date the new time in UTC milliseconds from the epoch.
 */
 void 
@@ -1058,9 +1061,19 @@ Calendar::setTimeInMillis( double millis, UErrorCode& status ) {
         return;
 
     if (millis > MAX_MILLIS) {
-        millis = MAX_MILLIS;
+        if(isLenient()) {
+            millis = MAX_MILLIS;
+        } else {
+                   status = U_ILLEGAL_ARGUMENT_ERROR;
+                   return;
+        }
     } else if (millis < MIN_MILLIS) {
-        millis = MIN_MILLIS;
+        if(isLenient()) {
+            millis = MIN_MILLIS;
+        } else {
+               status = U_ILLEGAL_ARGUMENT_ERROR;
+               return;
+        }
     }
 
     fTime = millis;
index 3bc77cf149448d637cf180f39e0964da8feba8e2..c2a635a87d1faed6148c35a7864528cfd28697dc 100644 (file)
@@ -1,6 +1,6 @@
 /*
 *******************************************************************************
-* Copyright (C) 1997-2010, International Business Machines Corporation and    *
+* Copyright (C) 1997-2012, International Business Machines Corporation and    *
 * others. All Rights Reserved.                                                *
 *******************************************************************************
 *
@@ -364,7 +364,7 @@ GregorianCalendar::setGregorianChange(UDate date, UErrorCode& status)
 
 
 void GregorianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) {
-    int32_t eyear, month, dayOfMonth, dayOfYear;
+    int32_t eyear, month, dayOfMonth, dayOfYear, unusedRemainder;
 
 
     if(U_FAILURE(status)) { 
@@ -386,7 +386,7 @@ void GregorianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& statu
         // The Julian epoch day (not the same as Julian Day)
         // is zero on Saturday December 30, 0 (Gregorian).
         int32_t julianEpochDay = julianDay - (kJan1_1JulianDay - 2);
-        eyear = (int32_t) ClockMath::floorDivide(4*julianEpochDay + 1464, 1461);
+               eyear = (int32_t) ClockMath::floorDivide((4.0*julianEpochDay) + 1464.0, (int32_t) 1461, unusedRemainder);
 
         // Compute the Julian calendar day number for January 1, eyear
         int32_t january1 = 365*(eyear-1) + ClockMath::floorDivide(eyear-1, (int32_t)4);
index 79470cdb81e53537ecacc46984551d7f94ff0c89..6852d6c14ff2fca49fd50a2890d5a1a33562addf 100644 (file)
@@ -182,6 +182,19 @@ class BasicTimeZone;
  * For example, subtracting 5 days from the date <code>September 12, 1996</code>
  * results in <code>September 7, 1996</code>.
  *
+ * <p><big><b>Supported range</b></big>
+ *
+ * <p>The allowable range of <code>Calendar</code> has been
+ * narrowed. <code>GregorianCalendar</code> used to attempt to support
+ * the range of dates with millisecond values from
+ * <code>Long.MIN_VALUE</code> to <code>Long.MAX_VALUE</code>.
+ * The new <code>Calendar</code> protocol specifies the
+ * maximum range of supportable dates as those having Julian day numbers
+ * of <code>-0x7F000000</code> to <code>+0x7F000000</code>. This
+ * corresponds to years from ~5,800,000 BCE to ~5,800,000 CE. Programmers
+ * should use the protected constants in <code>Calendar</code> to
+ * specify an extremely early or extremely late date.</p>
+ *
  * @stable ICU 2.0
  */
 class U_I18N_API Calendar : public UObject {
index 65e6f190e677fca8e2a8ce7d1db92c460b056cd6..d265158a87e4809faa3593d8ea05cf24bb985303 100644 (file)
@@ -481,7 +481,7 @@ static void TestCalendar()
 
 static void TestGetSetDateAPI()
 {
-    UCalendar *caldef = 0, *caldef2 = 0;
+    UCalendar *caldef = 0, *caldef2 = 0, *caldef3 = 0;
     UChar tzID[4];
     UDate d1;
     int32_t hour;
@@ -490,12 +490,15 @@ static void TestGetSetDateAPI()
     UErrorCode status=U_ZERO_ERROR;
     UDate d2= 837039928046.0;
     UChar temp[30];
+       double testMillis;
+       int32_t dateBit;
 
     log_verbose("\nOpening the calendars()\n");
     u_strcpy(tzID, fgGMTID);
     /*open the calendars used */
     caldef=ucal_open(tzID, u_strlen(tzID), "en_US", UCAL_TRADITIONAL, &status);
     caldef2=ucal_open(tzID, u_strlen(tzID), "en_US", UCAL_TRADITIONAL, &status);
+    caldef3=ucal_open(tzID, u_strlen(tzID), "en_US", UCAL_TRADITIONAL, &status);
     /*open the dateformat */
     /* this is supposed to open default date format, but later on it treats it like it is "en_US" 
        - very bad if you try to run the tests on machine where default locale is NOT "en_US" */
@@ -528,6 +531,53 @@ static void TestGetSetDateAPI()
         log_err("error in setMillis or getMillis\n");
     /*-------------------*/
     
+    /*testing large negative millis*/
+       /*test a previously failed millis and beyond the lower bounds - ICU trac #9403 */
+       // -184303902611600000.0         - just beyond lower bounds (#9403 sets U_ILLEGAL_ARGUMENT_ERROR in strict mode)
+       // -46447814188001000.0          - fixed by #9403
+
+    log_verbose("\nTesting very large valid millis & invalid setMillis values (in both strict & lienent modes) detected\n");
+
+       testMillis = -46447814188001000.0;      // point where floorDivide in handleComputeFields failed as per #9403
+       log_verbose("using value[%lf]\n", testMillis);
+    ucal_setAttribute(caldef3, UCAL_LENIENT, 0);
+    ucal_setMillis(caldef3, testMillis, &status);
+       if(U_FAILURE(status)){
+               log_err("Fail: setMillis incorrectly detected invalid value : for millis : %e : returned  : %s\n", testMillis, u_errorName(status));
+               status = U_ZERO_ERROR;
+       }
+
+    log_verbose("\nTesting invalid setMillis values detected\n");
+       testMillis = -184303902611600000.0;
+       log_verbose("using value[%lf]\n", testMillis);
+    ucal_setAttribute(caldef3, UCAL_LENIENT, 1);
+       ucal_setMillis(caldef3, testMillis, &status);
+       if(U_FAILURE(status)){
+               log_err("Fail: setMillis incorrectly detected invalid value : for millis : %e : returned  : %s\n", testMillis, u_errorName(status));
+               status = U_ZERO_ERROR;
+       } else {
+        dateBit = ucal_get(caldef2, UCAL_MILLISECOND, &status);
+        if(testMillis == dateBit)
+        {
+                   log_err("Fail: error in setMillis, allowed invalid value %e : returns millisecond : %d", testMillis, dateBit);
+        } else {
+            log_verbose("Pass: setMillis correctly pinned min, returned : %d", dateBit);
+        }
+       }
+
+    log_verbose("\nTesting invalid setMillis values detected\n");
+       testMillis = -184303902611600000.0;
+       log_verbose("using value[%lf]\n", testMillis);
+    ucal_setAttribute(caldef3, UCAL_LENIENT, 0);
+       ucal_setMillis(caldef3, testMillis, &status);
+       if(U_FAILURE(status)){
+               log_verbose("Pass: Illegal argument error as expected : for millis : %e : returned  : %s\n", testMillis, u_errorName(status));
+               status = U_ZERO_ERROR;
+       } else {
+               dateBit = ucal_get(caldef3, UCAL_DAY_OF_MONTH, &status);
+               log_err("Fail: error in setMillis, allowed invalid value %e : returns DayOfMonth : %d", testMillis, dateBit);
+       }
+       /*-------------------*/
     
     
     ctest_setTimeZone(NULL, &status);