]> granicus.if.org Git - icu/commitdiff
ICU-10544 Fixed some implementation problems in Calendar::add. When adding day or...
authorYoshito Umaoka <y.umaoka@gmail.com>
Tue, 25 Feb 2014 23:50:35 +0000 (23:50 +0000)
committerYoshito Umaoka <y.umaoka@gmail.com>
Tue, 25 Feb 2014 23:50:35 +0000 (23:50 +0000)
X-SVN-Rev: 35231

icu4c/source/i18n/calendar.cpp
icu4c/source/i18n/unicode/calendar.h
icu4c/source/test/intltest/caltest.cpp
icu4c/source/test/intltest/caltest.h

index 27825c04a2c85ec8907ebcdd03c9e23d382d02fc..9e7a3cc86d25602b3e225e5aa96dc9cfd6852e00 100644 (file)
@@ -1,6 +1,6 @@
 /*
 *******************************************************************************
-* Copyright (C) 1997-2013, International Business Machines Corporation and    *
+* Copyright (C) 1997-2014, International Business Machines Corporation and    *
 * others. All Rights Reserved.                                                *
 *******************************************************************************
 *
@@ -1894,10 +1894,10 @@ void Calendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status
     // a computed amount of millis to the current millis.  The only
     // wrinkle is with DST (and/or a change to the zone's UTC offset, which
     // we'll include with DST) -- for some fields, like the DAY_OF_MONTH,
-    // we don't want the HOUR to shift due to changes in DST.  If the
+    // we don't want the wall time to shift due to changes in DST.  If the
     // result of the add operation is to move from DST to Standard, or
     // vice versa, we need to adjust by an hour forward or back,
-    // respectively.  For such fields we set keepHourInvariant to TRUE.
+    // respectively.  For such fields we set keepWallTimeInvariant to TRUE.
 
     // We only adjust the DST for fields larger than an hour.  For
     // fields smaller than an hour, we cannot adjust for DST without
@@ -1912,7 +1912,7 @@ void Calendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status
     // <April 30>, rather than <April 31> => <May 1>.
 
     double delta = amount; // delta in ms
-    UBool keepHourInvariant = TRUE;
+    UBool keepWallTimeInvariant = TRUE;
 
     switch (field) {
     case UCAL_ERA:
@@ -1974,22 +1974,22 @@ void Calendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status
     case UCAL_HOUR_OF_DAY:
     case UCAL_HOUR:
         delta *= kOneHour;
-        keepHourInvariant = FALSE;
+        keepWallTimeInvariant = FALSE;
         break;
 
     case UCAL_MINUTE:
         delta *= kOneMinute;
-        keepHourInvariant = FALSE;
+        keepWallTimeInvariant = FALSE;
         break;
 
     case UCAL_SECOND:
         delta *= kOneSecond;
-        keepHourInvariant = FALSE;
+        keepWallTimeInvariant = FALSE;
         break;
 
     case UCAL_MILLISECOND:
     case UCAL_MILLISECONDS_IN_DAY:
-        keepHourInvariant = FALSE;
+        keepWallTimeInvariant = FALSE;
         break;
 
     default:
@@ -2003,41 +2003,61 @@ void Calendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status
         //                                     ") not supported");
     }
 
-    // In order to keep the hour invariant (for fields where this is
+    // In order to keep the wall time invariant (for fields where this is
     // appropriate), check the combined DST & ZONE offset before and
     // after the add() operation. If it changes, then adjust the millis
     // to compensate.
     int32_t prevOffset = 0;
-    int32_t hour = 0;
-    if (keepHourInvariant) {
+    int32_t prevWallTime = 0;
+    if (keepWallTimeInvariant) {
         prevOffset = get(UCAL_DST_OFFSET, status) + get(UCAL_ZONE_OFFSET, status);
-        hour = internalGet(UCAL_HOUR_OF_DAY);
+        prevWallTime = get(UCAL_MILLISECONDS_IN_DAY, status);
     }
 
     setTimeInMillis(getTimeInMillis(status) + delta, status);
 
-    if (keepHourInvariant) {
-        int32_t newOffset = get(UCAL_DST_OFFSET, status) + get(UCAL_ZONE_OFFSET, status);
-        if (newOffset != prevOffset) {
-            // We have done an hour-invariant adjustment but the
-            // combined offset has changed. We adjust millis to keep
-            // the hour constant. In cases such as midnight after
-            // a DST change which occurs at midnight, there is the
-            // danger of adjusting into a different day. To avoid
-            // this we make the adjustment only if it actually
-            // maintains the hour.
-
-            // When the difference of the previous UTC offset and
-            // the new UTC offset exceeds 1 full day, we do not want
-            // to roll over/back the date. For now, this only happens
-            // in Samoa (Pacific/Apia) on Dec 30, 2011. See ticket:9452.
-            int32_t adjAmount = prevOffset - newOffset;
-            adjAmount = adjAmount >= 0 ? adjAmount % (int32_t)kOneDay : -(-adjAmount % (int32_t)kOneDay);
-            if (adjAmount != 0) {
-                double t = internalGetTime();
-                setTimeInMillis(t + adjAmount, status);
-                if (get(UCAL_HOUR_OF_DAY, status) != hour) {
-                    setTimeInMillis(t, status);
+    if (keepWallTimeInvariant) {
+        int32_t newWallTime = get(UCAL_MILLISECONDS_IN_DAY, status);
+        if (newWallTime != prevWallTime) {
+            // There is at least one zone transition between the base
+            // time and the result time. As the result, wall time has
+            // changed.
+            UDate t = internalGetTime();
+            int32_t newOffset = get(UCAL_DST_OFFSET, status) + get(UCAL_ZONE_OFFSET, status);
+            if (newOffset != prevOffset) {
+                // When the difference of the previous UTC offset and
+                // the new UTC offset exceeds 1 full day, we do not want
+                // to roll over/back the date. For now, this only happens
+                // in Samoa (Pacific/Apia) on Dec 30, 2011. See ticket:9452.
+                int32_t adjAmount = prevOffset - newOffset;
+                adjAmount = adjAmount >= 0 ? adjAmount % (int32_t)kOneDay : -(-adjAmount % (int32_t)kOneDay);
+                if (adjAmount != 0) {
+                    setTimeInMillis(t + adjAmount, status);
+                    newWallTime = get(UCAL_MILLISECONDS_IN_DAY, status);
+                }
+                if (newWallTime != prevWallTime) {
+                    // The result wall time or adjusted wall time was shifted because
+                    // the target wall time does not exist on the result date.
+                    switch (fSkippedWallTime) {
+                    case UCAL_WALLTIME_FIRST:
+                        if (adjAmount > 0) {
+                            setTimeInMillis(t, status);
+                        }
+                        break;
+                    case UCAL_WALLTIME_LAST:
+                        if (adjAmount < 0) {
+                            setTimeInMillis(t, status);
+                        }
+                        break;
+                    case UCAL_WALLTIME_NEXT_VALID:
+                        UDate tmpT = adjAmount > 0 ? internalGetTime() : t;
+                        UDate immediatePrevTrans;
+                        UBool hasTransition = getImmediatePreviousZoneTransition(tmpT, &immediatePrevTrans, status);
+                        if (U_SUCCESS(status) && hasTransition) {
+                            setTimeInMillis(immediatePrevTrans, status);
+                        }
+                        break;
+                    }
                 }
             }
         }
@@ -2818,22 +2838,10 @@ void Calendar::computeTime(UErrorCode& status) {
                         // Adjust time to the next valid wall clock time.
                         // At this point, tmpTime is on or after the zone offset transition causing
                         // the skipped time range.
-
-                        BasicTimeZone *btz = getBasicTimeZone();
-                        if (btz) {
-                            TimeZoneTransition transition;
-                            UBool hasTransition = btz->getPreviousTransition(tmpTime, TRUE, transition);
-                            if (hasTransition) {
-                                t = transition.getTime();
-                            } else {
-                                // Could not find any transitions.
-                                // Note: This should never happen.
-                                status = U_INTERNAL_PROGRAM_ERROR;
-                            }
-                        } else {
-                            // If not BasicTimeZone, return unsupported error for now.
-                            // TODO: We may support non-BasicTimeZone in future.
-                            status = U_UNSUPPORTED_ERROR;
+                        UDate immediatePrevTransition;
+                        UBool hasTransition = getImmediatePreviousZoneTransition(tmpTime, &immediatePrevTransition, status);
+                        if (U_SUCCESS(status) && hasTransition) {
+                            t = immediatePrevTransition;
                         }
                     }
                 } else {
@@ -2849,6 +2857,30 @@ void Calendar::computeTime(UErrorCode& status) {
     }
 }
 
+/**
+ * Find the previous zone transtion near the given time.
+ */
+UBool Calendar::getImmediatePreviousZoneTransition(UDate base, UDate *transitionTime, UErrorCode& status) const {
+    BasicTimeZone *btz = getBasicTimeZone();
+    if (btz) {
+        TimeZoneTransition trans;
+        UBool hasTransition = btz->getPreviousTransition(base, TRUE, trans);
+        if (hasTransition) {
+            *transitionTime = trans.getTime();
+            return TRUE;
+        } else {
+            // Could not find any transitions.
+            // Note: This should never happen.
+            status = U_INTERNAL_PROGRAM_ERROR;
+        }
+    } else {
+        // If not BasicTimeZone, return unsupported error for now.
+        // TODO: We may support non-BasicTimeZone in future.
+        status = U_UNSUPPORTED_ERROR;
+    }
+    return FALSE;
+}
+
 /**
 * Compute the milliseconds in the day from the fields.  This is a
 * value from 0 to 23:59:59.999 inclusive, unless fields are out of
index b861e1b2052dcf44e6611cab70c48a0f6226190a..83d9140bf4cc404f8e3465f68db41d8320d5306c 100644 (file)
@@ -1,6 +1,6 @@
 /*
 ********************************************************************************
-*   Copyright (C) 1997-2013, International Business Machines
+*   Copyright (C) 1997-2014, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 ********************************************************************************
 *
@@ -2428,6 +2428,15 @@ private:
      * is not an instance of BasicTimeZone.
      */
     BasicTimeZone* getBasicTimeZone() const;
+
+    /**
+     * Find the previous zone transtion near the given time.
+     * @param base The base time, inclusive
+     * @param transitionTime Receives the result time
+     * @param status The error status
+     * @return TRUE if a transition is found.
+     */
+    UBool getImmediatePreviousZoneTransition(UDate base, UDate *transitionTime, UErrorCode& status) const;
 };
 
 // -------------------------------------
index c6849acf7e31cda4a603f2811a8e98ac3e9f8b45..c7647d57b0c7a64bc35beb464f377191a8bbd69a 100644 (file)
@@ -312,6 +312,13 @@ void CalendarTest::runIndexedTest( int32_t index, UBool exec, const char* &name,
             TestWeekData();
           }
           break;
+        case 35:
+          name = "TestAddAcrossZoneTransition";
+          if(exec) {
+            logln("TestAddAcrossZoneTransition---"); logln("");
+            TestAddAcrossZoneTransition();
+          }
+          break;
         default: name = ""; break;
     }
 }
@@ -2422,12 +2429,13 @@ CalendarTest::TestAmbiguousWallTimeAPIs(void) {
 
 class CalFields {
 public:
-    CalFields(int32_t year, int32_t month, int32_t day, int32_t hour, int32_t min, int32_t sec);
+    CalFields(int32_t year, int32_t month, int32_t day, int32_t hour, int32_t min, int32_t sec, int32_t ms = 0);
     CalFields(const Calendar& cal, UErrorCode& status);
     void setTo(Calendar& cal) const;
     char* toString(char* buf, int32_t len) const;
     UBool operator==(const CalFields& rhs) const;
     UBool operator!=(const CalFields& rhs) const;
+    UBool isEquivalentTo(const Calendar& cal, UErrorCode& status) const;
 
 private:
     int32_t year;
@@ -2436,10 +2444,11 @@ private:
     int32_t hour;
     int32_t min;
     int32_t sec;
+    int32_t ms;
 };
 
-CalFields::CalFields(int32_t year, int32_t month, int32_t day, int32_t hour, int32_t min, int32_t sec)
-    : year(year), month(month), day(day), hour(hour), min(min), sec(sec) {
+CalFields::CalFields(int32_t year, int32_t month, int32_t day, int32_t hour, int32_t min, int32_t sec, int32_t ms)
+    : year(year), month(month), day(day), hour(hour), min(min), sec(sec), ms(ms) {
 }
 
 CalFields::CalFields(const Calendar& cal, UErrorCode& status) {
@@ -2449,18 +2458,20 @@ CalFields::CalFields(const Calendar& cal, UErrorCode& status) {
     hour = cal.get(UCAL_HOUR_OF_DAY, status);
     min = cal.get(UCAL_MINUTE, status);
     sec = cal.get(UCAL_SECOND, status);
+    ms = cal.get(UCAL_MILLISECOND, status);
 }
 
 void
 CalFields::setTo(Calendar& cal) const {
     cal.clear();
     cal.set(year, month - 1, day, hour, min, sec);
+    cal.set(UCAL_MILLISECOND, ms);
 }
 
 char*
 CalFields::toString(char* buf, int32_t len) const {
     char local[32];
-    sprintf(local, "%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, min, sec);
+    sprintf(local, "%04d-%02d-%02d %02d:%02d:%02d.%03d", year, month, day, hour, min, sec, ms);
     uprv_strncpy(buf, local, len - 1);
     buf[len - 1] = 0;
     return buf;
@@ -2473,7 +2484,8 @@ CalFields::operator==(const CalFields& rhs) const {
         && day == rhs.day
         && hour == rhs.hour
         && min == rhs.min
-        && sec == rhs.sec;
+        && sec == rhs.sec
+        && ms == rhs.ms;
 }
 
 UBool
@@ -2481,6 +2493,17 @@ CalFields::operator!=(const CalFields& rhs) const {
     return !(*this == rhs);
 }
 
+UBool
+CalFields::isEquivalentTo(const Calendar& cal, UErrorCode& status) const {
+    return year == cal.get(UCAL_YEAR, status)
+        && month == cal.get(UCAL_MONTH, status) + 1
+        && day == cal.get(UCAL_DAY_OF_MONTH, status)
+        && hour == cal.get(UCAL_HOUR_OF_DAY, status)
+        && min == cal.get(UCAL_MINUTE, status)
+        && sec == cal.get(UCAL_SECOND, status)
+        && ms == cal.get(UCAL_MILLISECOND, status);
+}
+
 typedef struct {
     const char*     tzid;
     const CalFields in;
@@ -2969,6 +2992,183 @@ void CalendarTest::TestWeekData() {
     }
 }
 
+typedef struct {
+    const char* zone;
+    const CalFields base;
+    int32_t deltaDays;
+    UCalendarWallTimeOption skippedWTOpt;
+    const CalFields expected;
+} TestAddAcrossZoneTransitionData;
+
+static const TestAddAcrossZoneTransitionData AAZTDATA[] =
+{
+    // Time zone                Base wall time                      day(s)  Skipped time options
+    //                          Expected wall time
+    
+    // Add 1 day, from the date before DST transition
+    {"America/Los_Angeles",     CalFields(2014,3,8,1,59,59,999),    1,      UCAL_WALLTIME_FIRST,
+                                CalFields(2014,3,9,1,59,59,999)},
+
+    {"America/Los_Angeles",     CalFields(2014,3,8,1,59,59,999),    1,      UCAL_WALLTIME_LAST,
+                                CalFields(2014,3,9,1,59,59,999)},
+
+    {"America/Los_Angeles",     CalFields(2014,3,8,1,59,59,999),    1,      UCAL_WALLTIME_NEXT_VALID,
+                                CalFields(2014,3,9,1,59,59,999)},
+
+
+    {"America/Los_Angeles",     CalFields(2014,3,8,2,0,0,0),        1,      UCAL_WALLTIME_FIRST,
+                                CalFields(2014,3,9,1,0,0,0)},
+
+    {"America/Los_Angeles",     CalFields(2014,3,8,2,0,0,0),        1,      UCAL_WALLTIME_LAST,
+                                CalFields(2014,3,9,3,0,0,0)},
+
+    {"America/Los_Angeles",     CalFields(2014,3,8,2,0,0,0),        1,      UCAL_WALLTIME_NEXT_VALID,
+                                CalFields(2014,3,9,3,0,0,0)},
+
+
+    {"America/Los_Angeles",     CalFields(2014,3,8,2,30,0,0),       1,      UCAL_WALLTIME_FIRST,
+                                CalFields(2014,3,9,1,30,0,0)},
+
+    {"America/Los_Angeles",     CalFields(2014,3,8,2,30,0,0),       1,      UCAL_WALLTIME_LAST,
+                                CalFields(2014,3,9,3,30,0,0)},
+
+    {"America/Los_Angeles",     CalFields(2014,3,8,2,30,0,0),       1,      UCAL_WALLTIME_NEXT_VALID,
+                                CalFields(2014,3,9,3,0,0,0)},
+
+
+    {"America/Los_Angeles",     CalFields(2014,3,8,3,0,0,0),        1,      UCAL_WALLTIME_FIRST,
+                                CalFields(2014,3,9,3,0,0,0)},
+
+    {"America/Los_Angeles",     CalFields(2014,3,8,3,0,0,0),        1,      UCAL_WALLTIME_LAST,
+                                CalFields(2014,3,9,3,0,0,0)},
+
+    {"America/Los_Angeles",     CalFields(2014,3,8,3,0,0,0),        1,      UCAL_WALLTIME_NEXT_VALID,
+                                CalFields(2014,3,9,3,0,0,0)},
+
+    // Subtract 1 day, from one day after DST transition
+    {"America/Los_Angeles",     CalFields(2014,3,10,1,59,59,999),   -1,     UCAL_WALLTIME_FIRST,
+                                CalFields(2014,3,9,1,59,59,999)},
+
+    {"America/Los_Angeles",     CalFields(2014,3,10,1,59,59,999),   -1,     UCAL_WALLTIME_LAST,
+                                CalFields(2014,3,9,1,59,59,999)},
+
+    {"America/Los_Angeles",     CalFields(2014,3,10,1,59,59,999),   -1,     UCAL_WALLTIME_NEXT_VALID,
+                                CalFields(2014,3,9,1,59,59,999)},
+
+
+    {"America/Los_Angeles",     CalFields(2014,3,10,2,0,0,0),       -1,     UCAL_WALLTIME_FIRST,
+                                CalFields(2014,3,9,1,0,0,0)},
+
+    {"America/Los_Angeles",     CalFields(2014,3,10,2,0,0,0),       -1,     UCAL_WALLTIME_LAST,
+                                CalFields(2014,3,9,3,0,0,0)},
+
+    {"America/Los_Angeles",     CalFields(2014,3,10,2,0,0,0),       -1,     UCAL_WALLTIME_NEXT_VALID,
+                                CalFields(2014,3,9,3,0,0,0)},
+
+
+    {"America/Los_Angeles",     CalFields(2014,3,10,2,30,0,0),      -1,     UCAL_WALLTIME_FIRST,
+                                CalFields(2014,3,9,1,30,0,0)},
+
+    {"America/Los_Angeles",     CalFields(2014,3,10,2,30,0,0),      -1,     UCAL_WALLTIME_LAST,
+                                CalFields(2014,3,9,3,30,0,0)},
+
+    {"America/Los_Angeles",     CalFields(2014,3,10,2,30,0,0),      -1,     UCAL_WALLTIME_NEXT_VALID,
+                                CalFields(2014,3,9,3,0,0,0)},
+
+
+    {"America/Los_Angeles",     CalFields(2014,3,10,3,0,0,0),       -1,     UCAL_WALLTIME_FIRST,
+                                CalFields(2014,3,9,3,0,0,0)},
+
+    {"America/Los_Angeles",     CalFields(2014,3,10,3,0,0,0),       -1,     UCAL_WALLTIME_LAST,
+                                CalFields(2014,3,9,3,0,0,0)},
+
+    {"America/Los_Angeles",     CalFields(2014,3,10,3,0,0,0),       -1,     UCAL_WALLTIME_NEXT_VALID,
+                                CalFields(2014,3,9,3,0,0,0)},
+
+
+    // Test case for ticket#10544
+    {"America/Santiago",        CalFields(2013,4,27,0,0,0,0),       134,    UCAL_WALLTIME_FIRST,
+                                CalFields(2013,9,7,23,0,0,0)},
+
+    {"America/Santiago",        CalFields(2013,4,27,0,0,0,0),       134,    UCAL_WALLTIME_LAST,
+                                CalFields(2013,9,8,1,0,0,0)},
+
+    {"America/Santiago",        CalFields(2013,4,27,0,0,0,0),       134,    UCAL_WALLTIME_NEXT_VALID,
+                                CalFields(2013,9,8,1,0,0,0)},
+
+
+    {"America/Santiago",        CalFields(2013,4,27,0,30,0,0),      134,    UCAL_WALLTIME_FIRST,
+                                CalFields(2013,9,7,23,30,0,0)},
+
+    {"America/Santiago",        CalFields(2013,4,27,0,30,0,0),      134,    UCAL_WALLTIME_LAST,
+                                CalFields(2013,9,8,1,30,0,0)},
+
+    {"America/Santiago",        CalFields(2013,4,27,0,30,0,0),      134,    UCAL_WALLTIME_NEXT_VALID,
+                                CalFields(2013,9,8,1,0,0,0)},
+
+
+    // Extreme transition - Pacific/Apia completely skips 2011-12-30
+    {"Pacific/Apia",            CalFields(2011,12,29,0,0,0,0),      1,      UCAL_WALLTIME_FIRST,
+                                CalFields(2011,12,31,0,0,0,0)},
+
+    {"Pacific/Apia",            CalFields(2011,12,29,0,0,0,0),      1,      UCAL_WALLTIME_LAST,
+                                CalFields(2011,12,31,0,0,0,0)},
+
+    {"Pacific/Apia",            CalFields(2011,12,29,0,0,0,0),      1,      UCAL_WALLTIME_NEXT_VALID,
+                                CalFields(2011,12,31,0,0,0,0)},
+
+
+    {"Pacific/Apia",            CalFields(2011,12,31,12,0,0,0),     -1,     UCAL_WALLTIME_FIRST,
+                                CalFields(2011,12,29,12,0,0,0)},
+
+    {"Pacific/Apia",            CalFields(2011,12,31,12,0,0,0),     -1,     UCAL_WALLTIME_LAST,
+                                CalFields(2011,12,29,12,0,0,0)},
+
+    {"Pacific/Apia",            CalFields(2011,12,31,12,0,0,0),     -1,     UCAL_WALLTIME_NEXT_VALID,
+                                CalFields(2011,12,29,12,0,0,0)},
+
+
+    // 30 minutes DST - Australia/Lord_Howe
+    {"Australia/Lord_Howe",     CalFields(2013,10,5,2,15,0,0),      1,      UCAL_WALLTIME_FIRST,
+                                CalFields(2013,10,6,1,45,0,0)},
+
+    {"Australia/Lord_Howe",     CalFields(2013,10,5,2,15,0,0),      1,      UCAL_WALLTIME_LAST,
+                                CalFields(2013,10,6,2,45,0,0)},
+
+    {"Australia/Lord_Howe",     CalFields(2013,10,5,2,15,0,0),      1,      UCAL_WALLTIME_NEXT_VALID,
+                                CalFields(2013,10,6,2,30,0,0)},
+
+    {NULL, CalFields(0,0,0,0,0,0,0), 0, UCAL_WALLTIME_LAST, CalFields(0,0,0,0,0,0,0)}
+};
+
+void CalendarTest::TestAddAcrossZoneTransition() {
+    UErrorCode status = U_ZERO_ERROR;
+    GregorianCalendar cal(status);
+    TEST_CHECK_STATUS;
+
+    for (int32_t i = 0; AAZTDATA[i].zone; i++) {
+        status = U_ZERO_ERROR;
+        TimeZone *tz = TimeZone::createTimeZone(AAZTDATA[i].zone);
+        cal.adoptTimeZone(tz);
+        cal.setSkippedWallTimeOption(AAZTDATA[i].skippedWTOpt);
+        AAZTDATA[i].base.setTo(cal);
+        cal.add(UCAL_DATE, AAZTDATA[i].deltaDays, status);
+        TEST_CHECK_STATUS;
+
+        if (!AAZTDATA[i].expected.isEquivalentTo(cal, status)) {
+            CalFields res(cal, status);
+            TEST_CHECK_STATUS;
+            char buf[32];
+            const char *optDisp = AAZTDATA[i].skippedWTOpt == UCAL_WALLTIME_FIRST ? "FIRST" :
+                    AAZTDATA[i].skippedWTOpt == UCAL_WALLTIME_LAST ? "LAST" : "NEXT_VALID";
+            errln(UnicodeString("Error: base:") + AAZTDATA[i].base.toString(buf, sizeof(buf)) + ", tz:" + AAZTDATA[i].zone
+                        + ", delta:" + AAZTDATA[i].deltaDays + " day(s), opt:" + optDisp
+                        + ", result:" + res.toString(buf, sizeof(buf))
+                        + " - expected:" + AAZTDATA[i].expected.toString(buf, sizeof(buf)));
+        }
+    }
+}
+
 #endif /* #if !UCONFIG_NO_FORMATTING */
 
 //eof
index 1c428ef3604cfa3e2f9da13f0b58e825b3859b4d..0c9811657bd13bb819ff51d1a5f180cd5240d881 100644 (file)
@@ -249,6 +249,8 @@ public: // package
     void setAndTestWholeYear(Calendar* cal, int32_t startYear, UErrorCode& status);
 
     void TestWeekData(void);
+
+    void TestAddAcrossZoneTransition(void);
 };
 
 #endif /* #if !UCONFIG_NO_FORMATTING */