]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/test/intltest/dtfmtrtts.cpp
ICU-511.27.tar.gz
[apple/icu.git] / icuSources / test / intltest / dtfmtrtts.cpp
index 7ebe0186cc1d0be65a157b41ab86e1754f0c178c..b0ee96a6405331a42e39147f255825d649249f79 100644 (file)
@@ -1,8 +1,8 @@
-/********************************************************************
+/***********************************************************************
  * COPYRIGHT: 
  * COPYRIGHT: 
- * Copyright (c) 1997-2003, International Business Machines Corporation and
- * others. All Rights Reserved.
- ********************************************************************/
+ * Copyright (c) 1997-2012, International Business Machines Corporation
+ * and others. All Rights Reserved.
+ ***********************************************************************/
  
 #include "unicode/utypes.h"
 
  
 #include "unicode/utypes.h"
 
@@ -12,6 +12,7 @@
 #include "unicode/smpdtfmt.h"
 #include "unicode/gregocal.h"
 #include "dtfmtrtts.h"
 #include "unicode/smpdtfmt.h"
 #include "unicode/gregocal.h"
 #include "dtfmtrtts.h"
+#include "caltest.h"
 
 #include <stdio.h>
 #include <string.h>
 
 #include <stdio.h>
 #include <string.h>
 #define INFINITE 0
 #endif
 
 #define INFINITE 0
 #endif
 
+static const UVersionInfo ICU_452 = {4,5,2,0};
+
 // Define this to test just a single locale
 // Define this to test just a single locale
-//#define TEST_ONE_LOC  "ja_JP_TRADITIONAL"
+//#define TEST_ONE_LOC  "cs_CZ"
 
 // If SPARSENESS is > 0, we don't run each exhaustive possibility.
 // There are 24 total possible tests per each locale.  A SPARSENESS
 
 // If SPARSENESS is > 0, we don't run each exhaustive possibility.
 // There are 24 total possible tests per each locale.  A SPARSENESS
@@ -38,15 +41,22 @@ int32_t DateFormatRoundTripTest::SPARSENESS = 0;
 int32_t DateFormatRoundTripTest::TRIALS = 4;
 int32_t DateFormatRoundTripTest::DEPTH = 5;
 
 int32_t DateFormatRoundTripTest::TRIALS = 4;
 int32_t DateFormatRoundTripTest::DEPTH = 5;
 
+DateFormatRoundTripTest::DateFormatRoundTripTest() : dateFormat(0) {
+}
+
+DateFormatRoundTripTest::~DateFormatRoundTripTest() {
+    delete dateFormat;
+}
 
 #define CASE(id,test) case id: name = #test; if (exec) { logln(#test "---"); logln((UnicodeString)""); test(); } break;
 
 void 
 
 #define CASE(id,test) case id: name = #test; if (exec) { logln(#test "---"); logln((UnicodeString)""); test(); } break;
 
 void 
-DateFormatRoundTripTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
+DateFormatRoundTripTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* par )
 {
 {
-    // if (exec) logln((UnicodeString)"TestSuite NumberFormatRegressionTest");
+    optionv = (par && *par=='v');
     switch (index) {
         CASE(0,TestDateFormatRoundTrip)
     switch (index) {
         CASE(0,TestDateFormatRoundTrip)
+        CASE(1, TestCentury)
         default: name = ""; break;
     }
 }
         default: name = ""; break;
     }
 }
@@ -75,16 +85,56 @@ DateFormatRoundTripTest::failure(UErrorCode status, const char* msg, const Unico
     return FALSE;
 }
 
     return FALSE;
 }
 
+void DateFormatRoundTripTest::TestCentury()
+{
+    UErrorCode status = U_ZERO_ERROR;
+    Locale locale("es_PA");
+    UnicodeString pattern = "MM/dd/yy hh:mm:ss a z";
+    SimpleDateFormat fmt(pattern, locale, status);
+    if (U_FAILURE(status)) {
+        dataerrln("Fail: construct SimpleDateFormat: %s", u_errorName(status));
+        return;
+    }
+    UDate date[] = {-55018555891590.05, 0, 0};
+    UnicodeString result[2];
+
+    fmt.format(date[0], result[0]);
+    date[1] = fmt.parse(result[0], status);
+    fmt.format(date[1], result[1]);
+    date[2] = fmt.parse(result[1], status);
+
+    /* This test case worked OK by accident before.  date[1] != date[0],
+     * because we use -80/+20 year window for 2-digit year parsing.
+     * (date[0] is in year 1926, date[1] is in year 2026.)  result[1] set
+     * by the first format call returns "07/13/26 07:48:28 p.m. PST",
+     * which is correct, because DST was not used in year 1926 in zone
+     * America/Los_Angeles.  When this is parsed, date[1] becomes a time
+     * in 2026, which is "07/13/26 08:48:28 p.m. PDT".  There was a zone
+     * offset calculation bug that observed DST in 1926, which was resolved.
+     * Before the bug was resolved, result[0] == result[1] was true,
+     * but after the bug fix, the expected result is actually
+     * result[0] != result[1]. -Yoshito
+     */
+    /* TODO: We need to review this code and clarify what we really
+     * want to test here.
+     */
+    //if (date[1] != date[2] || result[0] != result[1]) {
+    if (date[1] != date[2]) {
+        errln("Round trip failure: \"%S\" (%f), \"%S\" (%f)", result[0].getBuffer(), date[1], result[1].getBuffer(), date[2]);
+    }
+}
+
 // ==
 
 void DateFormatRoundTripTest::TestDateFormatRoundTrip() 
 {
     UErrorCode status = U_ZERO_ERROR;
 // ==
 
 void DateFormatRoundTripTest::TestDateFormatRoundTrip() 
 {
     UErrorCode status = U_ZERO_ERROR;
-    dateFormat = new SimpleDateFormat((UnicodeString)"EEE MMM dd HH:mm:ss.SSS zzz yyyy G", status);
-    failure(status, "new SimpleDateFormat");
 
     getFieldCal = Calendar::createInstance(status);
 
     getFieldCal = Calendar::createInstance(status);
-    failure(status, "Calendar::createInstance");
+    if (U_FAILURE(status)) {
+        dataerrln("Fail: Calendar::createInstance: %s", u_errorName(status));
+        return;
+    }
 
 
     int32_t locCount = 0;
 
 
     int32_t locCount = 0;
@@ -118,13 +168,24 @@ void DateFormatRoundTripTest::TestDateFormatRoundTrip()
 # else
     test(Locale::getDefault());
 
 # else
     test(Locale::getDefault());
 
+#if 1
+    // installed locales
     for (int i=0; i < locCount; ++i) {
     for (int i=0; i < locCount; ++i) {
-        test(avail[i]);
+            test(avail[i]);
+    }
+#endif
+
+#if 1
+    // special locales
+    int32_t jCount = CalendarTest::testLocaleCount();
+    for (int32_t j=0; j < jCount; ++j) {
+        test(Locale(CalendarTest::testLocaleID(j)));
     }
     }
+#endif
+
 # endif
 #endif
 
 # endif
 #endif
 
-    delete dateFormat;
     delete getFieldCal;
 }
 
     delete getFieldCal;
 }
 
@@ -201,13 +262,13 @@ void DateFormatRoundTripTest::test(const Locale& loc)
     for(int32_t dstyle = DateFormat::FULL; dstyle <= DateFormat::SHORT; ++dstyle) {
         for(int32_t tstyle = DateFormat::FULL; tstyle <= DateFormat::SHORT; ++tstyle) {
             if(TEST_TABLE[itable++]) {
     for(int32_t dstyle = DateFormat::FULL; dstyle <= DateFormat::SHORT; ++dstyle) {
         for(int32_t tstyle = DateFormat::FULL; tstyle <= DateFormat::SHORT; ++tstyle) {
             if(TEST_TABLE[itable++]) {
-              logln("Testing dstyle" + UnicodeString(styleName((DateFormat::EStyle)dstyle)) + ", tstyle" + UnicodeString(styleName((DateFormat::EStyle)tstyle)) );
+                logln("Testing dstyle" + UnicodeString(styleName((DateFormat::EStyle)dstyle)) + ", tstyle" + UnicodeString(styleName((DateFormat::EStyle)tstyle)) );
                 DateFormat *df = DateFormat::createDateTimeInstance((DateFormat::EStyle)dstyle, (DateFormat::EStyle)tstyle, loc);
                 if(df == NULL) {
                 DateFormat *df = DateFormat::createDateTimeInstance((DateFormat::EStyle)dstyle, (DateFormat::EStyle)tstyle, loc);
                 if(df == NULL) {
-                  errln(UnicodeString("Could not DF::createDateTimeInstance ") + UnicodeString(styleName((DateFormat::EStyle)dstyle)) + ", tstyle" + UnicodeString(styleName((DateFormat::EStyle)tstyle))    + "Locale: " + loc.getDisplayName(temp));
+                    dataerrln(UnicodeString("Could not DF::createDateTimeInstance ") + UnicodeString(styleName((DateFormat::EStyle)dstyle)) + ", tstyle" + UnicodeString(styleName((DateFormat::EStyle)tstyle))    + "Locale: " + loc.getDisplayName(temp));
                 } else {
                 } else {
-                  test(df, loc);
-                  delete df;
+                    test(df, loc);
+                    delete df;
                 }
             }
         }
                 }
             }
         }
@@ -221,6 +282,15 @@ void DateFormatRoundTripTest::test(DateFormat *fmt, const Locale &origLocale, UB
         errln("DateFormat wasn't a SimpleDateFormat");
         return;
     }
         errln("DateFormat wasn't a SimpleDateFormat");
         return;
     }
+    
+    UBool isGregorian = FALSE;
+    UErrorCode minStatus = U_ZERO_ERROR;
+    UDate minDate = CalendarTest::minDateOfCalendar(*fmt->getCalendar(), isGregorian, minStatus);
+    if(U_FAILURE(minStatus)) {
+      errln((UnicodeString)"Failure getting min date for " + origLocale.getName());
+      return;
+    } 
+    //logln(UnicodeString("Min date is ") + fullFormat(minDate)  + " for " + origLocale.getName());
 
     pat = ((SimpleDateFormat*)fmt)->toPattern(pat);
 
 
     pat = ((SimpleDateFormat*)fmt)->toPattern(pat);
 
@@ -230,7 +300,8 @@ void DateFormatRoundTripTest::test(DateFormat *fmt, const Locale &origLocale, UB
     // patterns we have, but it may be a problem later.
 
     UBool hasEra = (pat.indexOf(UnicodeString("G")) != -1);
     // patterns we have, but it may be a problem later.
 
     UBool hasEra = (pat.indexOf(UnicodeString("G")) != -1);
-    UBool hasZone = (pat.indexOf(UnicodeString("z")) != -1);
+    UBool hasZoneDisplayName = (pat.indexOf(UnicodeString("z")) != -1) || (pat.indexOf(UnicodeString("v")) != -1) 
+        || (pat.indexOf(UnicodeString("V")) != -1);
 
     // Because patterns contain incomplete data representing the Date,
     // we must be careful of how we do the roundtrip.  We start with
 
     // Because patterns contain incomplete data representing the Date,
     // we must be careful of how we do the roundtrip.  We start with
@@ -246,7 +317,11 @@ void DateFormatRoundTripTest::test(DateFormat *fmt, const Locale &origLocale, UB
             UDate *d                = new UDate    [DEPTH];
             UnicodeString *s    = new UnicodeString[DEPTH];
 
             UDate *d                = new UDate    [DEPTH];
             UnicodeString *s    = new UnicodeString[DEPTH];
 
-            d[0] = generateDate();
+            if(isGregorian == TRUE) {
+              d[0] = generateDate();
+            } else {
+              d[0] = generateDate(minDate);
+            }
 
             UErrorCode status = U_ZERO_ERROR;
 
 
             UErrorCode status = U_ZERO_ERROR;
 
@@ -260,12 +335,15 @@ void DateFormatRoundTripTest::test(DateFormat *fmt, const Locale &origLocale, UB
             for(loop = 0; loop < DEPTH; ++loop) {
                 if (loop > 0)  {
                     d[loop] = fmt->parse(s[loop-1], status);
             for(loop = 0; loop < DEPTH; ++loop) {
                 if (loop > 0)  {
                     d[loop] = fmt->parse(s[loop-1], status);
-                    failure(status, "fmt->parse", s[loop-1]);
+                    failure(status, "fmt->parse", s[loop-1]+" in locale: " + origLocale.getName());
                     status = U_ZERO_ERROR; /* any error would have been reported */
                 }
 
                 s[loop] = fmt->format(d[loop], s[loop]);
                 
                     status = U_ZERO_ERROR; /* any error would have been reported */
                 }
 
                 s[loop] = fmt->format(d[loop], s[loop]);
                 
+                // For displaying which date is being tested
+                //logln(s[loop] + " = " + fullFormat(d[loop]));
+                
                 if(s[loop].length() == 0) {
                   errln("FAIL: fmt->format gave 0-length string in " + pat + " with number " + d[loop] + " in locale " + origLocale.getName());
                 }
                 if(s[loop].length() == 0) {
                   errln("FAIL: fmt->format gave 0-length string in " + pat + " with number " + d[loop] + " in locale " + origLocale.getName());
                 }
@@ -305,21 +383,35 @@ void DateFormatRoundTripTest::test(DateFormat *fmt, const Locale &origLocale, UB
             int maxSmatch = 1;
             if (dmatch > maxDmatch) {
                 // Time-only pattern with zone information and a starting date in PST.
             int maxSmatch = 1;
             if (dmatch > maxDmatch) {
                 // Time-only pattern with zone information and a starting date in PST.
-                if(timeOnly && hasZone && fmt->getTimeZone().inDaylightTime(d[0], status) && ! failure(status, "TimeZone::inDST()")) {
-                    maxDmatch = 3;
-                    maxSmatch = 2;
+                if(timeOnly && hasZoneDisplayName) {
+                    int32_t startRaw, startDst;
+                    fmt->getTimeZone().getOffset(d[0], FALSE, startRaw, startDst, status);
+                    failure(status, "TimeZone::getOffset");
+                    // if the start offset is greater than the offset on Jan 1, 1970
+                    // in PST, then need one more round trip.  There are two cases
+                    // fall into this category.  The start date is 1) DST or
+                    // 2) LMT (GMT-07:52:58).
+                    if (startRaw + startDst > -28800000) {
+                        maxDmatch = 3;
+                        maxSmatch = 2;
+                    }
                 }
             }
 
             // String usually matches in 1.  Exceptions are checked for here.
             if(smatch > maxSmatch) { // Don't compute unless necessary
                 }
             }
 
             // String usually matches in 1.  Exceptions are checked for here.
             if(smatch > maxSmatch) { // Don't compute unless necessary
+                UBool in0;
                 // Starts in BC, with no era in pattern
                 if( ! hasEra && getField(d[0], UCAL_ERA) == GregorianCalendar::BC)
                     maxSmatch = 2;
                 // Starts in DST, no year in pattern
                 // Starts in BC, with no era in pattern
                 if( ! hasEra && getField(d[0], UCAL_ERA) == GregorianCalendar::BC)
                     maxSmatch = 2;
                 // Starts in DST, no year in pattern
-                else if(fmt->getTimeZone().inDaylightTime(d[0], status) && ! failure(status, "gettingDaylightTime") &&
+                else if((in0=fmt->getTimeZone().inDaylightTime(d[0], status)) && ! failure(status, "gettingDaylightTime") &&
                          pat.indexOf(UnicodeString("yyyy")) == -1)
                     maxSmatch = 2;
                          pat.indexOf(UnicodeString("yyyy")) == -1)
                     maxSmatch = 2;
+                // If we start not in DST, but transition into DST
+                else if (!in0 &&
+                         fmt->getTimeZone().inDaylightTime(d[1], status) && !failure(status, "gettingDaylightTime"))
+                    maxSmatch = 2;
                 // Two digit year with no time zone change,
                 // unless timezone isn't used or we aren't close to the DST changover
                 else if (pat.indexOf(UnicodeString("y")) != -1
                 // Two digit year with no time zone change,
                 // unless timezone isn't used or we aren't close to the DST changover
                 else if (pat.indexOf(UnicodeString("y")) != -1
@@ -327,35 +419,55 @@ void DateFormatRoundTripTest::test(DateFormat *fmt, const Locale &origLocale, UB
                         && getField(d[0], UCAL_YEAR)
                             != getField(d[dmatch], UCAL_YEAR)
                         && !failure(status, "error status [smatch>maxSmatch]")
                         && getField(d[0], UCAL_YEAR)
                             != getField(d[dmatch], UCAL_YEAR)
                         && !failure(status, "error status [smatch>maxSmatch]")
-                        && ((hasZone
+                        && ((hasZoneDisplayName
                          && (fmt->getTimeZone().inDaylightTime(d[0], status)
                                 == fmt->getTimeZone().inDaylightTime(d[dmatch], status)
                             || getField(d[0], UCAL_MONTH) == UCAL_APRIL
                             || getField(d[0], UCAL_MONTH) == UCAL_OCTOBER))
                          && (fmt->getTimeZone().inDaylightTime(d[0], status)
                                 == fmt->getTimeZone().inDaylightTime(d[dmatch], status)
                             || getField(d[0], UCAL_MONTH) == UCAL_APRIL
                             || getField(d[0], UCAL_MONTH) == UCAL_OCTOBER))
-                         || !hasZone)
+                         || !hasZoneDisplayName)
                          )
                 {
                     maxSmatch = 2;
                          )
                 {
                     maxSmatch = 2;
-                }                
+                }
+                // If zone display name is used, fallback format might be used before 1970
+                else if (hasZoneDisplayName && d[0] < 0) {
+                    maxSmatch = 2;
+                }
             }
 
             }
 
-            if((dmatch > maxDmatch || smatch > maxSmatch) && // Special case for Japanese (could have large negative years)
-               !strcmp(fmt->getCalendar()->getType(),"japanese")) {
-                    maxSmatch = 4;
-                    maxDmatch = 4;
-            }
-            
+            /*
+             * Special case for Japanese and Buddhist (could have large negative years)
+             * Also, Hebrew calendar need help handling leap month.
+             */
             if(dmatch > maxDmatch || smatch > maxSmatch) {
             if(dmatch > maxDmatch || smatch > maxSmatch) {
-                errln(UnicodeString("Pattern: ") + pat + UnicodeString(" failed to match in Locale: ")
-                    + origLocale.getName());
-                logln(UnicodeString(" Date ") + dmatch + "  String " + smatch);
+              const char *type = fmt->getCalendar()->getType();
+              if(!strcmp(type,"japanese") || (!strcmp(type,"buddhist"))) {
+                maxSmatch = 4;
+                maxDmatch = 4;
+              } else if(!strcmp(type,"hebrew")) {
+                  maxSmatch = 3;
+                  maxDmatch = 3;
+                }
+            }
 
 
-                printf("dmatch:%d maxD:%d smatch:%d maxS:%d\n", dmatch,maxDmatch, smatch, maxSmatch);
+            // Use @v to see verbose results on successful cases
+            UBool fail = (dmatch > maxDmatch || smatch > maxSmatch);
+            if (optionv || fail) {
+                if (fail) {
+                    errln(UnicodeString("\nFAIL: Pattern: ") + pat +
+                          " in Locale: " + origLocale.getName());
+                } else {
+                    errln(UnicodeString("\nOk: Pattern: ") + pat +
+                          " in Locale: " + origLocale.getName());
+                }
                 
                 
+                logln("Date iters until match=%d (max allowed=%d), string iters until match=%d (max allowed=%d)",
+                      dmatch,maxDmatch, smatch, maxSmatch);
+
                 for(int j = 0; j <= loop && j < DEPTH; ++j) {
                     UnicodeString temp;
                     FieldPosition pos(FieldPosition::DONT_CARE);
                 for(int j = 0; j <= loop && j < DEPTH; ++j) {
                     UnicodeString temp;
                     FieldPosition pos(FieldPosition::DONT_CARE);
-                    errln((j>0?" P> ":"    ") + dateFormat->format(d[j], temp, pos) + " F> " +
+                    errln((j>0?" P> ":"    ") + fullFormat(d[j]) + " F> " +
                           escape(s[j], temp) + UnicodeString(" d=") + d[j] + 
                           (j > 0 && d[j]/*.getTime()*/==d[j-1]/*.getTime()*/?" d==":"") +
                           (j > 0 && s[j] == s[j-1]?" s==":""));
                           escape(s[j], temp) + UnicodeString(" d=") + d[j] + 
                           (j > 0 && d[j]/*.getTime()*/==d[j-1]/*.getTime()*/?" d==":"") +
                           (j > 0 && s[j] == s[j-1]?" s==":""));
@@ -371,6 +483,22 @@ void DateFormatRoundTripTest::test(DateFormat *fmt, const Locale &origLocale, UB
     }*/
 }
 
     }*/
 }
 
+const UnicodeString& DateFormatRoundTripTest::fullFormat(UDate d) {
+    UErrorCode ec = U_ZERO_ERROR;
+    if (dateFormat == 0) {
+        dateFormat = new SimpleDateFormat((UnicodeString)"EEE MMM dd HH:mm:ss.SSS zzz yyyy G", ec);
+        if (U_FAILURE(ec) || dateFormat == 0) {
+            fgStr = "[FAIL: SimpleDateFormat constructor]";
+            delete dateFormat;
+            dateFormat = 0;
+            return fgStr;
+        }
+    }
+    fgStr.truncate(0);
+    dateFormat->format(d, fgStr);
+    return fgStr;
+}
+
 /**
  * Return a field of the given date
  */
 /**
  * Return a field of the given date
  */
@@ -403,6 +531,33 @@ UnicodeString& DateFormatRoundTripTest::escape(const UnicodeString& src, Unicode
     return dst;
 }
 
     return dst;
 }
 
+#define U_MILLIS_PER_YEAR (365.25 * 24 * 60 * 60 * 1000)
+
+UDate DateFormatRoundTripTest::generateDate(UDate minDate)
+{
+  // Bring range in conformance to generateDate() below.
+  if(minDate < (U_MILLIS_PER_YEAR * -(4000-1970))) {
+    minDate = (U_MILLIS_PER_YEAR * -(4000-1970));
+  }
+  for(int i=0;i<8;i++) {
+    double a = randFraction();
+    
+    // Range from (min) to  (8000-1970) AD
+    double dateRange = (0.0 - minDate) + (U_MILLIS_PER_YEAR + (8000-1970));
+    
+    a *= dateRange;
+
+    // Now offset from minDate
+    a += minDate;
+   
+    // Last sanity check
+    if(a>=minDate) {
+      return a;
+    }
+  }
+  return minDate;
+}
+
 UDate DateFormatRoundTripTest::generateDate() 
 {
     double a = randFraction();
 UDate DateFormatRoundTripTest::generateDate() 
 {
     double a = randFraction();