1// Copyright (C) 2016 and later: Unicode, Inc. and others. 2// License & terms of use: http://www.unicode.org/copyright.html 3/*********************************************************************** 4 * Copyright (c) 1997-2009, International Business Machines Corporation 5 * and others. All Rights Reserved. 6 ***********************************************************************/ 7 8#include "unicode/utypes.h" 9 10#if !UCONFIG_NO_FORMATTING 11 12#include "unicode/datefmt.h" 13#include "unicode/smpdtfmt.h" 14#include "tsdate.h" 15#include "putilimp.h" 16 17#include <float.h> 18#include <stdlib.h> 19#include <math.h> 20 21const double IntlTestDateFormat::ONEYEAR = 365.25 * ONEDAY; // Approximate 22 23IntlTestDateFormat::~IntlTestDateFormat() {} 24 25/** 26 * This test does round-trip testing (format -> parse -> format -> parse -> etc.) of 27 * DateFormat. 28 */ 29// par is ignored throughout this file 30void IntlTestDateFormat::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ ) 31{ 32 if (exec) logln("TestSuite DateFormat"); 33 switch (index) { 34 case 0: name = "GenericTest"; 35 if (exec) { 36 logln(name); 37 fFormat = DateFormat::createInstance(); 38 fTestName = "createInstance"; 39 fLimit = 3; 40 testFormat(/* par */); 41 } 42 break; 43 case 1: name = "DefaultLocale"; 44 if (exec) { 45 logln(name); 46 testLocale(/*par, */Locale::getDefault(), "Default Locale"); 47 } 48 break; 49 50 case 2: name = "TestAvailableLocales"; 51 if (exec) { 52 logln(name); 53 testAvailableLocales(/* par */); 54 } 55 break; 56 57 case 3: name = "MonsterTest"; 58 if (exec) { 59 logln(name); 60 monsterTest(/*par*/); 61 } 62 break; 63 64 default: name = ""; break; 65 } 66} 67 68void 69IntlTestDateFormat::testLocale(/*char* par, */const Locale& locale, const UnicodeString& localeName) 70{ 71 DateFormat::EStyle timeStyle, dateStyle; 72 73 // For patterns including only time information and a timezone, it may take 74 // up to three iterations, since the timezone may shift as the year number 75 // is determined. For other patterns, 2 iterations should suffice. 76 fLimit = 3; 77 78 for(timeStyle = (DateFormat::EStyle)0; 79 timeStyle < (DateFormat::EStyle)4; 80 timeStyle = (DateFormat::EStyle) (timeStyle+1)) 81 { 82 fTestName = (UnicodeString) "Time test " + (int32_t) timeStyle + " (" + localeName + ")"; 83 fFormat = DateFormat::createTimeInstance(timeStyle, locale); 84 testFormat(/* par */); 85 } 86 87 fLimit = 2; 88 89 for(dateStyle = (DateFormat::EStyle)0; 90 dateStyle < (DateFormat::EStyle)4; 91 dateStyle = (DateFormat::EStyle) (dateStyle+1)) 92 { 93 fTestName = (UnicodeString) "Date test " + (int32_t) dateStyle + " (" + localeName + ")"; 94 fFormat = DateFormat::createDateInstance(dateStyle, locale); 95 testFormat(/* par */); 96 } 97 98 for(dateStyle = (DateFormat::EStyle)0; 99 dateStyle < (DateFormat::EStyle)4; 100 dateStyle = (DateFormat::EStyle) (dateStyle+1)) 101 { 102 for(timeStyle = (DateFormat::EStyle)0; 103 timeStyle < (DateFormat::EStyle)4; 104 timeStyle = (DateFormat::EStyle) (timeStyle+1)) 105 { 106 fTestName = (UnicodeString) "DateTime test " + (int32_t) dateStyle + "/" + (int32_t) timeStyle + " (" + localeName + ")"; 107 fFormat = DateFormat::createDateTimeInstance(dateStyle, timeStyle, locale); 108 testFormat(/* par */); 109 } 110 } 111} 112 113void IntlTestDateFormat::testFormat(/* char* par */) 114{ 115 if (fFormat == 0) 116 { 117 dataerrln("FAIL: DateFormat creation failed"); 118 return; 119 } 120 121 describeTest(); 122 123 UDate now = Calendar::getNow(); 124 tryDate(0); 125 tryDate(1278161801778.0); 126 tryDate(5264498352317.0); // Sunday, October 28, 2136 8:39:12 AM PST 127 tryDate(9516987689250.0); // In the year 2271 128 tryDate(now); 129 // Shift 6 months into the future, AT THE SAME TIME OF DAY. 130 // This will test the DST handling. 131 tryDate(now + 6.0*30*ONEDAY); 132 133 UDate limit = now * 10; // Arbitrary limit 134 for (int32_t i=0; i<3; ++i) 135 tryDate(uprv_floor(randDouble() * limit)); 136 137 delete fFormat; 138} 139 140void 141IntlTestDateFormat::describeTest() 142{ 143 // Assume it's a SimpleDateFormat and get some info 144 SimpleDateFormat *s = (SimpleDateFormat*)fFormat; 145 UnicodeString str; 146 logln(fTestName + " Pattern " + s->toPattern(str)); 147} 148 149void IntlTestDateFormat::tryDate(UDate theDate) 150{ 151 const int32_t DEPTH = 10; 152 UDate date[DEPTH]; 153 UnicodeString string[DEPTH]; 154 155 int32_t dateMatch = 0; 156 int32_t stringMatch = 0; 157 UBool dump = FALSE; 158#if defined (U_CAL_DEBUG) 159 dump = TRUE; 160#endif 161 int32_t i; 162 163 date[0] = theDate; 164 fFormat->format(theDate, string[0]); 165 166 for (i=1; i<DEPTH; ++i) 167 { 168 UErrorCode status = U_ZERO_ERROR; 169 date[i] = fFormat->parse(string[i-1], status); 170 if (U_FAILURE(status)) 171 { 172 describeTest(); 173 errln("**** FAIL: Parse of " + prettify(string[i-1], FALSE) + " failed."); 174 dump = TRUE; 175 break; 176 } 177 fFormat->format(date[i], string[i]); 178 if (dateMatch == 0 && date[i] == date[i-1]) 179 dateMatch = i; 180 else if (dateMatch > 0 && date[i] != date[i-1]) 181 { 182 describeTest(); 183 errln("**** FAIL: Date mismatch after match for " + string[i]); 184 dump = TRUE; 185 break; 186 } 187 if (stringMatch == 0 && string[i] == string[i-1]) 188 stringMatch = i; 189 else if (stringMatch > 0 && string[i] != string[i-1]) 190 { 191 describeTest(); 192 errln("**** FAIL: String mismatch after match for " + string[i]); 193 dump = TRUE; 194 break; 195 } 196 if (dateMatch > 0 && stringMatch > 0) 197 break; 198 } 199 if (i == DEPTH) 200 --i; 201 202 if (stringMatch > fLimit || dateMatch > fLimit) 203 { 204 describeTest(); 205 errln((UnicodeString)"**** FAIL: No string and/or date match within " + fLimit 206 + " iterations for the Date " + string[0] + "\t(" + theDate + ")."); 207 dump = TRUE; 208 } 209 210 if (dump) 211 { 212 for (int32_t k=0; k<=i; ++k) 213 { 214 logln((UnicodeString)"" + k + ": " + date[k] + " F> " + 215 string[k] + " P> "); 216 } 217 } 218} 219 220// Return a random double from 0.01 to 1, inclusive 221double IntlTestDateFormat::randDouble() 222{ 223 // Assume 8-bit (or larger) rand values. Also assume 224 // that the system rand() function is very poor, which it always is. 225 double d=0.0; 226 uint32_t i; 227 char* poke = (char*)&d; 228 do { 229 do { 230 for (i=0; i < sizeof(double); ++i) 231 { 232 poke[i] = (char)(rand() & 0xFF); 233 } 234 } while (uprv_isNaN(d) || uprv_isInfinite(d)); 235 236 if (d < 0.0) 237 d = -d; 238 if (d > 0.0) 239 { 240 double e = uprv_floor(log10(d)); 241 if (e < -2.0) 242 d *= uprv_pow10((int32_t)(-e-2)); 243 else if (e > -1.0) 244 d /= uprv_pow10((int32_t)(e+1)); 245 } 246 // While this is not a real normalized number make another one. 247 } while (uprv_isNaN(d) || uprv_isInfinite(d) 248 || !((-DBL_MAX < d && d < DBL_MAX) || (d < -DBL_MIN && DBL_MIN < d))); 249 return d; 250} 251 252void IntlTestDateFormat::testAvailableLocales(/* char* par */) 253{ 254 int32_t count = 0; 255 const Locale* locales = DateFormat::getAvailableLocales(count); 256 logln((UnicodeString)"" + count + " available locales"); 257 if (locales && count) 258 { 259 UnicodeString name; 260 UnicodeString all; 261 for (int32_t i=0; i<count; ++i) 262 { 263 if (i!=0) all += ", "; 264 all += locales[i].getName(); 265 } 266 logln(all); 267 } 268 else dataerrln((UnicodeString)"**** FAIL: Zero available locales or null array pointer"); 269} 270 271void IntlTestDateFormat::monsterTest(/*char *par*/) 272{ 273 int32_t count; 274 const Locale* locales = DateFormat::getAvailableLocales(count); 275 if (locales && count) 276 { 277 if (quick && count > 3) { 278 logln("quick test: testing just 3 locales!"); 279 count = 3; 280 } 281 for (int32_t i=0; i<count; ++i) 282 { 283 UnicodeString name = UnicodeString(locales[i].getName(), ""); 284 logln((UnicodeString)"Testing " + name + "..."); 285 testLocale(/*par, */locales[i], name); 286 } 287 } 288} 289 290#endif /* #if !UCONFIG_NO_FORMATTING */ 291