• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***********************************************************************
2  * COPYRIGHT:
3  * Copyright (c) 1997-2014, International Business Machines Corporation
4  * and others. All Rights Reserved.
5  ***********************************************************************/
6 
7 #include "unicode/utypes.h"
8 
9 #if !UCONFIG_NO_FORMATTING
10 
11 #include "unicode/timezone.h"
12 #include "unicode/simpletz.h"
13 #include "unicode/calendar.h"
14 #include "unicode/gregocal.h"
15 #include "unicode/resbund.h"
16 #include "unicode/strenum.h"
17 #include "unicode/uversion.h"
18 #include "tztest.h"
19 #include "cmemory.h"
20 #include "putilimp.h"
21 #include "cstring.h"
22 #include "olsontz.h"
23 
24 #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
25 
26 #define CASE(id,test) case id:                               \
27                           name = #test;                      \
28                           if (exec) {                        \
29                               logln(#test "---"); logln(""); \
30                               test();                        \
31                           }                                  \
32                           break
33 
34 // *****************************************************************************
35 // class TimeZoneTest
36 // *****************************************************************************
37 
38 // Some test case data is current date/tzdata version sensitive and producing errors
39 // when year/rule are changed. Although we want to keep our eyes on test failures
40 // caused by tzdata changes while development, keep maintaining test data in maintenance
41 // stream is a little bit hassle. ICU 49 or later versions are using minor version field
42 // to indicate a development build (0) or official release build (others). For development
43 // builds, a test failure triggers an error, while release builds only report them in
44 // verbose mode with logln.
45 static UBool isDevelopmentBuild = (U_ICU_VERSION_MINOR_NUM == 0);
46 
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)47 void TimeZoneTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
48 {
49     if (exec) {
50         logln("TestSuite TestTimeZone");
51     }
52     TESTCASE_AUTO_BEGIN;
53     TESTCASE_AUTO(TestPRTOffset);
54     TESTCASE_AUTO(TestVariousAPI518);
55     TESTCASE_AUTO(TestGetAvailableIDs913);
56     TESTCASE_AUTO(TestGenericAPI);
57     TESTCASE_AUTO(TestRuleAPI);
58     TESTCASE_AUTO(TestShortZoneIDs);
59     TESTCASE_AUTO(TestCustomParse);
60     TESTCASE_AUTO(TestDisplayName);
61     TESTCASE_AUTO(TestDSTSavings);
62     TESTCASE_AUTO(TestAlternateRules);
63     TESTCASE_AUTO(TestCountries);
64     TESTCASE_AUTO(TestHistorical);
65     TESTCASE_AUTO(TestEquivalentIDs);
66     TESTCASE_AUTO(TestAliasedNames);
67     TESTCASE_AUTO(TestFractionalDST);
68     TESTCASE_AUTO(TestFebruary);
69     TESTCASE_AUTO(TestCanonicalIDAPI);
70     TESTCASE_AUTO(TestCanonicalID);
71     TESTCASE_AUTO(TestDisplayNamesMeta);
72     TESTCASE_AUTO(TestGetRegion);
73     TESTCASE_AUTO(TestGetAvailableIDsNew);
74     TESTCASE_AUTO(TestGetUnknown);
75     TESTCASE_AUTO(TestGetWindowsID);
76     TESTCASE_AUTO(TestGetIDForWindowsID);
77     TESTCASE_AUTO_END;
78 }
79 
80 const int32_t TimeZoneTest::millisPerHour = 3600000;
81 
82 // ---------------------------------------------------------------------------------
83 
84 /**
85  * Generic API testing for API coverage.
86  */
87 void
TestGenericAPI()88 TimeZoneTest::TestGenericAPI()
89 {
90     UnicodeString id("NewGMT");
91     int32_t offset = 12345;
92 
93     SimpleTimeZone *zone = new SimpleTimeZone(offset, id);
94     if (zone->useDaylightTime()) errln("FAIL: useDaylightTime should return FALSE");
95 
96     TimeZone* zoneclone = zone->clone();
97     if (!(*zoneclone == *zone)) errln("FAIL: clone or operator== failed");
98     zoneclone->setID("abc");
99     if (!(*zoneclone != *zone)) errln("FAIL: clone or operator!= failed");
100     delete zoneclone;
101 
102     zoneclone = zone->clone();
103     if (!(*zoneclone == *zone)) errln("FAIL: clone or operator== failed");
104     zoneclone->setRawOffset(45678);
105     if (!(*zoneclone != *zone)) errln("FAIL: clone or operator!= failed");
106 
107     SimpleTimeZone copy(*zone);
108     if (!(copy == *zone)) errln("FAIL: copy constructor or operator== failed");
109     copy = *(SimpleTimeZone*)zoneclone;
110     if (!(copy == *zoneclone)) errln("FAIL: assignment operator or operator== failed");
111 
112     TimeZone* saveDefault = TimeZone::createDefault();
113     logln((UnicodeString)"TimeZone::createDefault() => " + saveDefault->getID(id));
114 
115     TimeZone::adoptDefault(zone);
116     TimeZone* defaultzone = TimeZone::createDefault();
117     if (defaultzone == zone ||
118         !(*defaultzone == *zone))
119         errln("FAIL: createDefault failed");
120     TimeZone::adoptDefault(saveDefault);
121     delete defaultzone;
122     delete zoneclone;
123 
124     logln("call uprv_timezone() which uses the host");
125     logln("to get the difference in seconds between coordinated universal");
126     logln("time and local time. E.g., -28,800 for PST (GMT-8hrs)");
127 
128     int32_t tzoffset = uprv_timezone();
129     if ((tzoffset % 900) != 0) {
130         /*
131          * Ticket#6364 and #7648
132          * A few time zones are using GMT offests not a multiple of 15 minutes.
133          * Therefore, we should not interpret such case as an error.
134          * We downgrade this from errln to infoln. When we see this message,
135          * we should examine if it is ignorable or not.
136          */
137         infoln("WARNING: t_timezone may be incorrect. It is not a multiple of 15min.", tzoffset);
138     }
139 
140     UErrorCode status = U_ZERO_ERROR;
141     const char* tzver = TimeZone::getTZDataVersion(status);
142     if (U_FAILURE(status)) {
143         errcheckln(status, "FAIL: getTZDataVersion failed - %s", u_errorName(status));
144     } else if (uprv_strlen(tzver) != 5 /* 4 digits + 1 letter */) {
145         errln((UnicodeString)"FAIL: getTZDataVersion returned " + tzver);
146     } else {
147         logln((UnicodeString)"tzdata version: " + tzver);
148     }
149 }
150 
151 // ---------------------------------------------------------------------------------
152 
153 /**
154  * Test the setStartRule/setEndRule API calls.
155  */
156 void
TestRuleAPI()157 TimeZoneTest::TestRuleAPI()
158 {
159     UErrorCode status = U_ZERO_ERROR;
160 
161     UDate offset = 60*60*1000*1.75; // Pick a weird offset
162     SimpleTimeZone *zone = new SimpleTimeZone((int32_t)offset, "TestZone");
163     if (zone->useDaylightTime()) errln("FAIL: useDaylightTime should return FALSE");
164 
165     // Establish our expected transition times.  Do this with a non-DST
166     // calendar with the (above) declared local offset.
167     GregorianCalendar *gc = new GregorianCalendar(*zone, status);
168     if (failure(status, "new GregorianCalendar", TRUE)) return;
169     gc->clear();
170     gc->set(1990, UCAL_MARCH, 1);
171     UDate marchOneStd = gc->getTime(status); // Local Std time midnight
172     gc->clear();
173     gc->set(1990, UCAL_JULY, 1);
174     UDate julyOneStd = gc->getTime(status); // Local Std time midnight
175     if (failure(status, "GregorianCalendar::getTime")) return;
176 
177     // Starting and ending hours, WALL TIME
178     int32_t startHour = (int32_t)(2.25 * 3600000);
179     int32_t endHour   = (int32_t)(3.5  * 3600000);
180 
181     zone->setStartRule(UCAL_MARCH, 1, 0, startHour, status);
182     zone->setEndRule  (UCAL_JULY,  1, 0, endHour, status);
183 
184     delete gc;
185     gc = new GregorianCalendar(*zone, status);
186     if (failure(status, "new GregorianCalendar")) return;
187 
188     UDate marchOne = marchOneStd + startHour;
189     UDate julyOne = julyOneStd + endHour - 3600000; // Adjust from wall to Std time
190 
191     UDate expMarchOne = 636251400000.0;
192     if (marchOne != expMarchOne)
193     {
194         errln((UnicodeString)"FAIL: Expected start computed as " + marchOne +
195           " = " + dateToString(marchOne));
196         logln((UnicodeString)"      Should be                  " + expMarchOne +
197           " = " + dateToString(expMarchOne));
198     }
199 
200     UDate expJulyOne = 646793100000.0;
201     if (julyOne != expJulyOne)
202     {
203         errln((UnicodeString)"FAIL: Expected start computed as " + julyOne +
204           " = " + dateToString(julyOne));
205         logln((UnicodeString)"      Should be                  " + expJulyOne +
206           " = " + dateToString(expJulyOne));
207     }
208 
209     testUsingBinarySearch(*zone, date(90, UCAL_JANUARY, 1), date(90, UCAL_JUNE, 15), marchOne);
210     testUsingBinarySearch(*zone, date(90, UCAL_JUNE, 1), date(90, UCAL_DECEMBER, 31), julyOne);
211 
212     if (zone->inDaylightTime(marchOne - 1000, status) ||
213         !zone->inDaylightTime(marchOne, status))
214         errln("FAIL: Start rule broken");
215     if (!zone->inDaylightTime(julyOne - 1000, status) ||
216         zone->inDaylightTime(julyOne, status))
217         errln("FAIL: End rule broken");
218 
219     zone->setStartYear(1991);
220     if (zone->inDaylightTime(marchOne, status) ||
221         zone->inDaylightTime(julyOne - 1000, status))
222         errln("FAIL: Start year broken");
223 
224     failure(status, "TestRuleAPI");
225     delete gc;
226     delete zone;
227 }
228 
229 void
findTransition(const TimeZone & tz,UDate min,UDate max)230 TimeZoneTest::findTransition(const TimeZone& tz,
231                              UDate min, UDate max) {
232     UErrorCode ec = U_ZERO_ERROR;
233     UnicodeString id,s;
234     UBool startsInDST = tz.inDaylightTime(min, ec);
235     if (failure(ec, "TimeZone::inDaylightTime")) return;
236     if (tz.inDaylightTime(max, ec) == startsInDST) {
237         logln("Error: " + tz.getID(id) + ".inDaylightTime(" + dateToString(min) + ") = " + (startsInDST?"TRUE":"FALSE") +
238               ", inDaylightTime(" + dateToString(max) + ") = " + (startsInDST?"TRUE":"FALSE"));
239         return;
240     }
241     if (failure(ec, "TimeZone::inDaylightTime")) return;
242     while ((max - min) > INTERVAL) {
243         UDate mid = (min + max) / 2;
244         if (tz.inDaylightTime(mid, ec) == startsInDST) {
245             min = mid;
246         } else {
247             max = mid;
248         }
249         if (failure(ec, "TimeZone::inDaylightTime")) return;
250     }
251     min = 1000.0 * uprv_floor(min/1000.0);
252     max = 1000.0 * uprv_floor(max/1000.0);
253     logln(tz.getID(id) + " Before: " + min/1000 + " = " +
254           dateToString(min,s,tz));
255     logln(tz.getID(id) + " After:  " + max/1000 + " = " +
256           dateToString(max,s,tz));
257 }
258 
259 void
testUsingBinarySearch(const TimeZone & tz,UDate min,UDate max,UDate expectedBoundary)260 TimeZoneTest::testUsingBinarySearch(const TimeZone& tz,
261                                     UDate min, UDate max,
262                                     UDate expectedBoundary)
263 {
264     UErrorCode status = U_ZERO_ERROR;
265     UBool startsInDST = tz.inDaylightTime(min, status);
266     if (failure(status, "TimeZone::inDaylightTime")) return;
267     if (tz.inDaylightTime(max, status) == startsInDST) {
268         logln("Error: inDaylightTime(" + dateToString(max) + ") != " + ((!startsInDST)?"TRUE":"FALSE"));
269         return;
270     }
271     if (failure(status, "TimeZone::inDaylightTime")) return;
272     while ((max - min) > INTERVAL) {
273         UDate mid = (min + max) / 2;
274         if (tz.inDaylightTime(mid, status) == startsInDST) {
275             min = mid;
276         } else {
277             max = mid;
278         }
279         if (failure(status, "TimeZone::inDaylightTime")) return;
280     }
281     logln(UnicodeString("Binary Search Before: ") + uprv_floor(0.5 + min) + " = " + dateToString(min));
282     logln(UnicodeString("Binary Search After:  ") + uprv_floor(0.5 + max) + " = " + dateToString(max));
283     UDate mindelta = expectedBoundary - min;
284     UDate maxdelta = max - expectedBoundary;
285     if (mindelta >= 0 &&
286         mindelta <= INTERVAL &&
287         maxdelta >= 0 &&
288         maxdelta <= INTERVAL)
289         logln(UnicodeString("PASS: Expected bdry:  ") + expectedBoundary + " = " + dateToString(expectedBoundary));
290     else
291         errln(UnicodeString("FAIL: Expected bdry:  ") + expectedBoundary + " = " + dateToString(expectedBoundary));
292 }
293 
294 const UDate TimeZoneTest::INTERVAL = 100;
295 
296 // ---------------------------------------------------------------------------------
297 
298 // -------------------------------------
299 
300 /**
301  * Test the offset of the PRT timezone.
302  */
303 void
TestPRTOffset()304 TimeZoneTest::TestPRTOffset()
305 {
306     TimeZone* tz = TimeZone::createTimeZone("PRT");
307     if (tz == 0) {
308         errln("FAIL: TimeZone(PRT) is null");
309     }
310     else {
311       int32_t expectedHour = -4;
312       double expectedOffset = (((double)expectedHour) * millisPerHour);
313       double foundOffset = tz->getRawOffset();
314       int32_t foundHour = (int32_t)foundOffset / millisPerHour;
315       if (expectedOffset != foundOffset) {
316         dataerrln("FAIL: Offset for PRT should be %d, found %d", expectedHour, foundHour);
317       } else {
318         logln("PASS: Offset for PRT should be %d, found %d", expectedHour, foundHour);
319       }
320     }
321     delete tz;
322 }
323 
324 // -------------------------------------
325 
326 /**
327  * Regress a specific bug with a sequence of API calls.
328  */
329 void
TestVariousAPI518()330 TimeZoneTest::TestVariousAPI518()
331 {
332     UErrorCode status = U_ZERO_ERROR;
333     TimeZone* time_zone = TimeZone::createTimeZone("PST");
334     UDate d = date(97, UCAL_APRIL, 30);
335     UnicodeString str;
336     logln("The timezone is " + time_zone->getID(str));
337     if (!time_zone->inDaylightTime(d, status)) dataerrln("FAIL: inDaylightTime returned FALSE");
338     if (failure(status, "TimeZone::inDaylightTime", TRUE)) return;
339     if (!time_zone->useDaylightTime()) dataerrln("FAIL: useDaylightTime returned FALSE");
340     if (time_zone->getRawOffset() != - 8 * millisPerHour) dataerrln("FAIL: getRawOffset returned wrong value");
341     GregorianCalendar *gc = new GregorianCalendar(status);
342     if (U_FAILURE(status)) { errln("FAIL: Couldn't create GregorianCalendar"); return; }
343     gc->setTime(d, status);
344     if (U_FAILURE(status)) { errln("FAIL: GregorianCalendar::setTime failed"); return; }
345     if (time_zone->getOffset(gc->AD, gc->get(UCAL_YEAR, status), gc->get(UCAL_MONTH, status),
346         gc->get(UCAL_DATE, status), (uint8_t)gc->get(UCAL_DAY_OF_WEEK, status), 0, status) != - 7 * millisPerHour)
347         dataerrln("FAIL: getOffset returned wrong value");
348     if (U_FAILURE(status)) { errln("FAIL: GregorianCalendar::set failed"); return; }
349     delete gc;
350     delete time_zone;
351 }
352 
353 // -------------------------------------
354 
355 /**
356  * Test the call which retrieves the available IDs.
357  */
358 void
TestGetAvailableIDs913()359 TimeZoneTest::TestGetAvailableIDs913()
360 {
361     UErrorCode ec = U_ZERO_ERROR;
362     int32_t i;
363 
364 #ifdef U_USE_TIMEZONE_OBSOLETE_2_8
365     // Test legacy API -- remove these tests when the corresponding API goes away (duh)
366     int32_t numIDs = -1;
367     const UnicodeString** ids = TimeZone::createAvailableIDs(numIDs);
368     if (ids == 0 || numIDs < 1) {
369         errln("FAIL: createAvailableIDs()");
370     } else {
371         UnicodeString buf("TimeZone::createAvailableIDs() = { ");
372         for(i=0; i<numIDs; ++i) {
373             if (i) buf.append(", ");
374             buf.append(*ids[i]);
375         }
376         buf.append(" } ");
377         logln(buf + numIDs);
378         // we own the array; the caller owns the contained strings (yuck)
379         uprv_free(ids);
380     }
381 
382     numIDs = -1;
383     ids = TimeZone::createAvailableIDs(-8*U_MILLIS_PER_HOUR, numIDs);
384     if (ids == 0 || numIDs < 1) {
385         errln("FAIL: createAvailableIDs(-8:00)");
386     } else {
387         UnicodeString buf("TimeZone::createAvailableIDs(-8:00) = { ");
388         for(i=0; i<numIDs; ++i) {
389             if (i) buf.append(", ");
390             buf.append(*ids[i]);
391         }
392         buf.append(" } ");
393         logln(buf + numIDs);
394         // we own the array; the caller owns the contained strings (yuck)
395         uprv_free(ids);
396     }
397     numIDs = -1;
398     ids = TimeZone::createAvailableIDs("US", numIDs);
399     if (ids == 0 || numIDs < 1) {
400       errln("FAIL: createAvailableIDs(US) ids=%d, numIDs=%d", ids, numIDs);
401     } else {
402         UnicodeString buf("TimeZone::createAvailableIDs(US) = { ");
403         for(i=0; i<numIDs; ++i) {
404             if (i) buf.append(", ");
405             buf.append(*ids[i]);
406         }
407         buf.append(" } ");
408         logln(buf + numIDs);
409         // we own the array; the caller owns the contained strings (yuck)
410         uprv_free(ids);
411     }
412 #endif
413 
414     UnicodeString str;
415     UnicodeString *buf = new UnicodeString("TimeZone::createEnumeration() = { ");
416     int32_t s_length;
417     StringEnumeration* s = TimeZone::createEnumeration();
418     if (s == NULL) {
419         dataerrln("Unable to create TimeZone enumeration");
420         return;
421     }
422     s_length = s->count(ec);
423     for (i = 0; i < s_length;++i) {
424         if (i > 0) *buf += ", ";
425         if ((i & 1) == 0) {
426             *buf += *s->snext(ec);
427         } else {
428             *buf += UnicodeString(s->next(NULL, ec), "");
429         }
430 
431         if((i % 5) == 4) {
432             // replace s with a clone of itself
433             StringEnumeration *s2 = s->clone();
434             if(s2 == NULL || s_length != s2->count(ec)) {
435                 errln("TimezoneEnumeration.clone() failed");
436             } else {
437                 delete s;
438                 s = s2;
439             }
440         }
441     }
442     *buf += " };";
443     logln(*buf);
444 
445     /* Confirm that the following zones can be retrieved: The first
446      * zone, the last zone, and one in-between.  This tests the binary
447      * search through the system zone data.
448      */
449     s->reset(ec);
450     int32_t middle = s_length/2;
451     for (i=0; i<s_length; ++i) {
452         const UnicodeString* id = s->snext(ec);
453         if (i==0 || i==middle || i==(s_length-1)) {
454         TimeZone *z = TimeZone::createTimeZone(*id);
455         if (z == 0) {
456             errln(UnicodeString("FAIL: createTimeZone(") +
457                   *id + ") -> 0");
458         } else if (z->getID(str) != *id) {
459             errln(UnicodeString("FAIL: createTimeZone(") +
460                   *id + ") -> zone " + str);
461         } else {
462             logln(UnicodeString("OK: createTimeZone(") +
463                   *id + ") succeeded");
464         }
465         delete z;
466         }
467     }
468     delete s;
469 
470     buf->truncate(0);
471     *buf += "TimeZone::createEnumeration(GMT+01:00) = { ";
472 
473     s = TimeZone::createEnumeration(1 * U_MILLIS_PER_HOUR);
474     s_length = s->count(ec);
475     for (i = 0; i < s_length;++i) {
476         if (i > 0) *buf += ", ";
477         *buf += *s->snext(ec);
478     }
479     delete s;
480     *buf += " };";
481     logln(*buf);
482 
483 
484     buf->truncate(0);
485     *buf += "TimeZone::createEnumeration(US) = { ";
486 
487     s = TimeZone::createEnumeration("US");
488     s_length = s->count(ec);
489     for (i = 0; i < s_length;++i) {
490         if (i > 0) *buf += ", ";
491         *buf += *s->snext(ec);
492     }
493     *buf += " };";
494     logln(*buf);
495 
496     TimeZone *tz = TimeZone::createTimeZone("PST");
497     if (tz != 0) logln("getTimeZone(PST) = " + tz->getID(str));
498     else errln("FAIL: getTimeZone(PST) = null");
499     delete tz;
500     tz = TimeZone::createTimeZone("America/Los_Angeles");
501     if (tz != 0) logln("getTimeZone(America/Los_Angeles) = " + tz->getID(str));
502     else errln("FAIL: getTimeZone(PST) = null");
503     delete tz;
504 
505     // @bug 4096694
506     tz = TimeZone::createTimeZone("NON_EXISTENT");
507     UnicodeString temp;
508     if (tz == 0)
509         errln("FAIL: getTimeZone(NON_EXISTENT) = null");
510     else if (tz->getID(temp) != UCAL_UNKNOWN_ZONE_ID)
511         errln("FAIL: getTimeZone(NON_EXISTENT) = " + temp);
512     delete tz;
513 
514     delete buf;
515     delete s;
516 }
517 
518 void
TestGetAvailableIDsNew()519 TimeZoneTest::TestGetAvailableIDsNew()
520 {
521     UErrorCode ec = U_ZERO_ERROR;
522     StringEnumeration *any, *canonical, *canonicalLoc;
523     StringEnumeration *any_US, *canonical_US, *canonicalLoc_US;
524     StringEnumeration *any_W5, *any_CA_W5;
525     StringEnumeration *any_US_E14;
526     int32_t rawOffset;
527     const UnicodeString *id1, *id2;
528     UnicodeString canonicalID;
529     UBool isSystemID;
530     char region[4];
531     int32_t zoneCount;
532 
533     any = canonical = canonicalLoc = any_US = canonical_US = canonicalLoc_US = any_W5 = any_CA_W5 = any_US_E14 = NULL;
534 
535     any = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, NULL, NULL, ec);
536     if (U_FAILURE(ec)) {
537         dataerrln("Failed to create enumration for ANY");
538         goto cleanup;
539     }
540 
541     canonical = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, ec);
542     if (U_FAILURE(ec)) {
543         errln("Failed to create enumration for CANONICAL");
544         goto cleanup;
545     }
546 
547     canonicalLoc = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, NULL, NULL, ec);
548     if (U_FAILURE(ec)) {
549         errln("Failed to create enumration for CANONICALLOC");
550         goto cleanup;
551     }
552 
553     any_US = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, "US", NULL, ec);
554     if (U_FAILURE(ec)) {
555         errln("Failed to create enumration for ANY_US");
556         goto cleanup;
557     }
558 
559     canonical_US = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, "US", NULL, ec);
560     if (U_FAILURE(ec)) {
561         errln("Failed to create enumration for CANONICAL_US");
562         goto cleanup;
563     }
564 
565     canonicalLoc_US = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, "US", NULL, ec);
566     if (U_FAILURE(ec)) {
567         errln("Failed to create enumration for CANONICALLOC_US");
568         goto cleanup;
569     }
570 
571     rawOffset = (-5)*60*60*1000;
572     any_W5 = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, NULL, &rawOffset, ec);
573     if (U_FAILURE(ec)) {
574         errln("Failed to create enumration for ANY_W5");
575         goto cleanup;
576     }
577 
578     any_CA_W5 = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, "CA", &rawOffset, ec);
579     if (U_FAILURE(ec)) {
580         errln("Failed to create enumration for ANY_CA_W5");
581         goto cleanup;
582     }
583 
584     rawOffset = 14*60*60*1000;
585     any_US_E14 = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, "US", &rawOffset, ec);
586     if (U_FAILURE(ec)) {
587         errln("Failed to create enumration for ANY_US_E14");
588         goto cleanup;
589     }
590 
591     checkContainsAll(any, "ANY", canonical, "CANONICAL");
592     checkContainsAll(canonical, "CANONICAL", canonicalLoc, "CANONICALLOC");
593 
594     checkContainsAll(any, "ANY", any_US, "ANY_US");
595     checkContainsAll(canonical, "CANONICAL", canonical_US, "CANONICAL_US");
596     checkContainsAll(canonicalLoc, "CANONICALLOC", canonicalLoc_US, "CANONICALLOC_US");
597 
598     checkContainsAll(any_US, "ANY_US", canonical_US, "CANONICAL_US");
599     checkContainsAll(canonical_US, "CANONICAL_US", canonicalLoc_US, "CANONICALLOC_US");
600 
601     checkContainsAll(any, "ANY", any_W5, "ANY_W5");
602     checkContainsAll(any_W5, "ANY_W5", any_CA_W5, "ANY_CA_W5");
603 
604     // And ID in any set, but not in canonical set must not be a canonical ID
605     any->reset(ec);
606     while ((id1 = any->snext(ec)) != NULL) {
607         UBool found = FALSE;
608         canonical->reset(ec);
609         while ((id2 = canonical->snext(ec)) != NULL) {
610             if (*id1 == *id2) {
611                 found = TRUE;
612                 break;
613             }
614         }
615         if (U_FAILURE(ec)) {
616             break;
617         }
618         if (!found) {
619             TimeZone::getCanonicalID(*id1, canonicalID, isSystemID, ec);
620             if (U_FAILURE(ec)) {
621                 break;
622             }
623             if (*id1 == canonicalID) {
624                 errln((UnicodeString)"FAIL: canonicalID [" + *id1 + "] is not in CANONICAL");
625             }
626             if (!isSystemID) {
627                 errln((UnicodeString)"FAIL: ANY contains non-system ID: " + *id1);
628             }
629         }
630     }
631     if (U_FAILURE(ec)) {
632         errln("Error checking IDs in ANY, but not in CANONICAL");
633         ec = U_ZERO_ERROR;
634     }
635 
636     // canonical set must contains only canonical IDs
637     canonical->reset(ec);
638     while ((id1 = canonical->snext(ec)) != NULL) {
639         TimeZone::getCanonicalID(*id1, canonicalID, isSystemID, ec);
640         if (U_FAILURE(ec)) {
641             break;
642         }
643         if (*id1 != canonicalID) {
644             errln((UnicodeString)"FAIL: CANONICAL contains non-canonical ID: " + *id1);
645         }
646         if (!isSystemID) {
647             errln((UnicodeString)"FAILE: CANONICAL contains non-system ID: " + *id1);
648         }
649     }
650     if (U_FAILURE(ec)) {
651         errln("Error checking IDs in CANONICAL");
652         ec = U_ZERO_ERROR;
653     }
654 
655     // canonicalLoc set must contain only canonical location IDs
656     canonicalLoc->reset(ec);
657     while ((id1 = canonicalLoc->snext(ec)) != NULL) {
658         TimeZone::getRegion(*id1, region, sizeof(region), ec);
659         if (U_FAILURE(ec)) {
660             break;
661         }
662         if (uprv_strcmp(region, "001") == 0) {
663             errln((UnicodeString)"FAIL: CANONICALLOC contains non location zone: " + *id1);
664         }
665     }
666     if (U_FAILURE(ec)) {
667         errln("Error checking IDs in CANONICALLOC");
668         ec = U_ZERO_ERROR;
669     }
670 
671     // any_US must contain only US zones
672     any_US->reset(ec);
673     while ((id1 = any_US->snext(ec)) != NULL) {
674         TimeZone::getRegion(*id1, region, sizeof(region), ec);
675         if (U_FAILURE(ec)) {
676             break;
677         }
678         if (uprv_strcmp(region, "US") != 0) {
679             errln((UnicodeString)"FAIL: ANY_US contains non-US zone ID: " + *id1);
680         }
681     }
682     if (U_FAILURE(ec)) {
683         errln("Error checking IDs in ANY_US");
684         ec = U_ZERO_ERROR;
685     }
686 
687     // any_W5 must contain only GMT-05:00 zones
688     any_W5->reset(ec);
689     while ((id1 = any_W5->snext(ec)) != NULL) {
690         TimeZone *tz = TimeZone::createTimeZone(*id1);
691         if (tz->getRawOffset() != (-5)*60*60*1000) {
692             errln((UnicodeString)"FAIL: ANY_W5 contains a zone whose offset is not -05:00: " + *id1);
693         }
694         delete tz;
695     }
696     if (U_FAILURE(ec)) {
697         errln("Error checking IDs in ANY_W5");
698         ec = U_ZERO_ERROR;
699     }
700 
701     // No US zone swith GMT+14:00
702     zoneCount = any_US_E14->count(ec);
703     if (U_FAILURE(ec)) {
704         errln("Error checking IDs in ANY_US_E14");
705         ec = U_ZERO_ERROR;
706     } else if (zoneCount != 0) {
707         errln("FAIL: ANY_US_E14 must be empty");
708     }
709 
710 cleanup:
711     delete any;
712     delete canonical;
713     delete canonicalLoc;
714     delete any_US;
715     delete canonical_US;
716     delete canonicalLoc_US;
717     delete any_W5;
718     delete any_CA_W5;
719     delete any_US_E14;
720 }
721 
722 void
checkContainsAll(StringEnumeration * s1,const char * name1,StringEnumeration * s2,const char * name2)723 TimeZoneTest::checkContainsAll(StringEnumeration *s1, const char *name1,
724         StringEnumeration *s2, const char *name2)
725 {
726     UErrorCode ec = U_ZERO_ERROR;
727     const UnicodeString *id1, *id2;
728 
729     s2->reset(ec);
730 
731     while ((id2 = s2->snext(ec)) != NULL) {
732         UBool found = FALSE;
733         s1->reset(ec);
734         while ((id1 = s1->snext(ec)) != NULL) {
735             if (*id1 == *id2) {
736                 found = TRUE;
737                 break;
738             }
739         }
740         if (!found) {
741             errln((UnicodeString)"FAIL: " + name1 + "does not contain "
742                 + *id2 + " in " + name2);
743         }
744     }
745 
746     if (U_FAILURE(ec)) {
747         errln((UnicodeString)"Error checkContainsAll for " + name1 + " - " + name2);
748     }
749 }
750 
751 /**
752  * NOTE: As of ICU 2.8, this test confirms that the "tz.alias"
753  * file, used to build ICU alias zones, is working.  It also
754  * looks at some genuine Olson compatibility IDs. [aliu]
755  *
756  * This test is problematic. It should really just confirm that
757  * the list of compatibility zone IDs exist and are somewhat
758  * meaningful (that is, they aren't all aliases of GMT). It goes a
759  * bit further -- it hard-codes expectations about zone behavior,
760  * when in fact zones are redefined quite frequently. ICU's build
761  * process means that it is easy to update ICU to contain the
762  * latest Olson zone data, but if a zone tested here changes, then
763  * this test will fail.  I have updated the test for 1999j data,
764  * but further updates will probably be required. Note that some
765  * of the concerts listed below no longer apply -- in particular,
766  * we do NOT overwrite real UNIX zones with 3-letter IDs. There
767  * are two points of overlap as of 1999j: MET and EET. These are
768  * both real UNIX zones, so we just use the official
769  * definition. This test has been updated to reflect this.
770  * 12/3/99 aliu
771  *
772  * Added tests for additional zones and aliases from the icuzones file.
773  * Markus Scherer 2006-nov-06
774  *
775  * [srl - from java - 7/5/1998]
776  * @bug 4130885
777  * Certain short zone IDs, used since 1.1.x, are incorrect.
778  *
779  * The worst of these is:
780  *
781  * "CAT" (Central African Time) should be GMT+2:00, but instead returns a
782  * zone at GMT-1:00. The zone at GMT-1:00 should be called EGT, CVT, EGST,
783  * or AZOST, depending on which zone is meant, but in no case is it CAT.
784  *
785  * Other wrong zone IDs:
786  *
787  * ECT (European Central Time) GMT+1:00: ECT is Ecuador Time,
788  * GMT-5:00. European Central time is abbreviated CEST.
789  *
790  * SST (Solomon Island Time) GMT+11:00. SST is actually Samoa Standard Time,
791  * GMT-11:00. Solomon Island time is SBT.
792  *
793  * NST (New Zealand Time) GMT+12:00. NST is the abbreviation for
794  * Newfoundland Standard Time, GMT-3:30. New Zealanders use NZST.
795  *
796  * AST (Alaska Standard Time) GMT-9:00. [This has already been noted in
797  * another bug.] It should be "AKST". AST is Atlantic Standard Time,
798  * GMT-4:00.
799  *
800  * PNT (Phoenix Time) GMT-7:00. PNT usually means Pitcairn Time,
801  * GMT-8:30. There is no standard abbreviation for Phoenix time, as distinct
802  * from MST with daylight savings.
803  *
804  * In addition to these problems, a number of zones are FAKE. That is, they
805  * don't match what people use in the real world.
806  *
807  * FAKE zones:
808  *
809  * EET (should be EEST)
810  * ART (should be EEST)
811  * MET (should be IRST)
812  * NET (should be AMST)
813  * PLT (should be PKT)
814  * BST (should be BDT)
815  * VST (should be ICT)
816  * CTT (should be CST) +
817  * ACT (should be CST) +
818  * AET (should be EST) +
819  * MIT (should be WST) +
820  * IET (should be EST) +
821  * PRT (should be AST) +
822  * CNT (should be NST)
823  * AGT (should be ARST)
824  * BET (should be EST) +
825  *
826  * + A zone with the correct name already exists and means something
827  * else. E.g., EST usually indicates the US Eastern zone, so it cannot be
828  * used for Brazil (BET).
829  */
TestShortZoneIDs()830 void TimeZoneTest::TestShortZoneIDs()
831 {
832     int32_t i;
833     // Create a small struct to hold the array
834     struct
835     {
836         const char *id;
837         int32_t    offset;
838         UBool      daylight;
839     }
840     kReferenceList [] =
841     {
842         {"HST", -600, FALSE}, // Olson northamerica -10:00
843         {"AST", -540, TRUE},  // ICU Link - America/Anchorage
844         {"PST", -480, TRUE},  // ICU Link - America/Los_Angeles
845         {"PNT", -420, FALSE}, // ICU Link - America/Phoenix
846         {"MST", -420, FALSE}, // updated Aug 2003 aliu
847         {"CST", -360, TRUE},  // Olson northamerica -7:00
848         {"IET", -300, TRUE},  // ICU Link - America/Indiana/Indianapolis
849         {"EST", -300, FALSE}, // Olson northamerica -5:00
850         {"PRT", -240, FALSE}, // ICU Link - America/Puerto_Rico
851         {"CNT", -210, TRUE},  // ICU Link - America/St_Johns
852         {"AGT", -180, FALSE}, // ICU Link - America/Argentina/Buenos_Aires
853         {"BET", -180, TRUE},  // ICU Link - America/Sao_Paulo
854         {"GMT", 0, FALSE},    // Olson etcetera Link - Etc/GMT
855         {"UTC", 0, FALSE},    // Olson etcetera 0
856         {"ECT", 60, TRUE},    // ICU Link - Europe/Paris
857         {"MET", 60, TRUE},    // Olson europe 1:00 C-Eur
858         {"CAT", 120, FALSE},  // ICU Link - Africa/Harare
859         {"ART", 120, FALSE},  // ICU Link - Africa/Cairo
860         {"EET", 120, TRUE},   // Olson europe 2:00 EU
861         {"EAT", 180, FALSE},  // ICU Link - Africa/Addis_Ababa
862         {"NET", 240, FALSE},  // ICU Link - Asia/Yerevan
863         {"PLT", 300, FALSE},  // ICU Link - Asia/Karachi
864         {"IST", 330, FALSE},  // ICU Link - Asia/Kolkata
865         {"BST", 360, FALSE},  // ICU Link - Asia/Dhaka
866         {"VST", 420, FALSE},  // ICU Link - Asia/Ho_Chi_Minh
867         {"CTT", 480, FALSE},  // ICU Link - Asia/Shanghai
868         {"JST", 540, FALSE},  // ICU Link - Asia/Tokyo
869         {"ACT", 570, FALSE},  // ICU Link - Australia/Darwin
870         {"AET", 600, TRUE},   // ICU Link - Australia/Sydney
871         {"SST", 660, FALSE},  // ICU Link - Pacific/Guadalcanal
872         {"NST", 720, TRUE},   // ICU Link - Pacific/Auckland
873         {"MIT", 780, TRUE},   // ICU Link - Pacific/Apia
874 
875         {"Etc/Unknown", 0, FALSE},  // CLDR
876 
877         {"SystemV/AST4ADT", -240, TRUE},
878         {"SystemV/EST5EDT", -300, TRUE},
879         {"SystemV/CST6CDT", -360, TRUE},
880         {"SystemV/MST7MDT", -420, TRUE},
881         {"SystemV/PST8PDT", -480, TRUE},
882         {"SystemV/YST9YDT", -540, TRUE},
883         {"SystemV/AST4", -240, FALSE},
884         {"SystemV/EST5", -300, FALSE},
885         {"SystemV/CST6", -360, FALSE},
886         {"SystemV/MST7", -420, FALSE},
887         {"SystemV/PST8", -480, FALSE},
888         {"SystemV/YST9", -540, FALSE},
889         {"SystemV/HST10", -600, FALSE},
890 
891         {"",0,FALSE}
892     };
893 
894     for(i=0;kReferenceList[i].id[0];i++) {
895         UnicodeString itsID(kReferenceList[i].id);
896         UBool ok = TRUE;
897         // Check existence.
898         TimeZone *tz = TimeZone::createTimeZone(itsID);
899         if (!tz || (kReferenceList[i].offset != 0 && *tz == *TimeZone::getGMT())) {
900             errln("FAIL: Time Zone " + itsID + " does not exist!");
901             continue;
902         }
903 
904         // Check daylight usage.
905         UBool usesDaylight = tz->useDaylightTime();
906         if (usesDaylight != kReferenceList[i].daylight) {
907             if (!isDevelopmentBuild) {
908                 logln("Warning: Time Zone " + itsID + " use daylight is " +
909                       (usesDaylight?"TRUE":"FALSE") +
910                       " but it should be " +
911                       ((kReferenceList[i].daylight)?"TRUE":"FALSE"));
912             } else {
913                 dataerrln("FAIL: Time Zone " + itsID + " use daylight is " +
914                       (usesDaylight?"TRUE":"FALSE") +
915                       " but it should be " +
916                       ((kReferenceList[i].daylight)?"TRUE":"FALSE"));
917             }
918             ok = FALSE;
919         }
920 
921         // Check offset
922         int32_t offsetInMinutes = tz->getRawOffset()/60000;
923         if (offsetInMinutes != kReferenceList[i].offset) {
924             if (!isDevelopmentBuild) {
925                 logln("FAIL: Time Zone " + itsID + " raw offset is " +
926                       offsetInMinutes +
927                       " but it should be " + kReferenceList[i].offset);
928             } else {
929                 dataerrln("FAIL: Time Zone " + itsID + " raw offset is " +
930                       offsetInMinutes +
931                       " but it should be " + kReferenceList[i].offset);
932             }
933             ok = FALSE;
934         }
935 
936         if (ok) {
937             logln("OK: " + itsID +
938                   " useDaylightTime() & getRawOffset() as expected");
939         }
940         delete tz;
941     }
942 
943 
944     // OK now test compat
945     logln("Testing for compatibility zones");
946 
947     const char* compatibilityMap[] = {
948         // This list is copied from tz.alias.  If tz.alias
949         // changes, this list must be updated.  Current as of Mar 2007
950         "ACT", "Australia/Darwin",
951         "AET", "Australia/Sydney",
952         "AGT", "America/Buenos_Aires",
953         "ART", "Africa/Cairo",
954         "AST", "America/Anchorage",
955         "BET", "America/Sao_Paulo",
956         "BST", "Asia/Dhaka", // # spelling changed in 2000h; was Asia/Dacca
957         "CAT", "Africa/Harare",
958         "CNT", "America/St_Johns",
959         "CST", "America/Chicago",
960         "CTT", "Asia/Shanghai",
961         "EAT", "Africa/Addis_Ababa",
962         "ECT", "Europe/Paris",
963         // EET Europe/Istanbul # EET is a standard UNIX zone
964         // "EST", "America/New_York", # Defined as -05:00
965         // "HST", "Pacific/Honolulu", # Defined as -10:00
966         "IET", "America/Indianapolis",
967         "IST", "Asia/Calcutta",
968         "JST", "Asia/Tokyo",
969         // MET Asia/Tehran # MET is a standard UNIX zone
970         "MIT", "Pacific/Apia",
971         // "MST", "America/Denver", # Defined as -07:00
972         "NET", "Asia/Yerevan",
973         "NST", "Pacific/Auckland",
974         "PLT", "Asia/Karachi",
975         "PNT", "America/Phoenix",
976         "PRT", "America/Puerto_Rico",
977         "PST", "America/Los_Angeles",
978         "SST", "Pacific/Guadalcanal",
979         "UTC", "Etc/GMT",
980         "VST", "Asia/Saigon",
981          "","",""
982     };
983 
984     for (i=0;*compatibilityMap[i];i+=2) {
985         UnicodeString itsID;
986 
987         const char *zone1 = compatibilityMap[i];
988         const char *zone2 = compatibilityMap[i+1];
989 
990         TimeZone *tz1 = TimeZone::createTimeZone(zone1);
991         TimeZone *tz2 = TimeZone::createTimeZone(zone2);
992 
993         if (!tz1) {
994             errln(UnicodeString("FAIL: Could not find short ID zone ") + zone1);
995         }
996         if (!tz2) {
997             errln(UnicodeString("FAIL: Could not find long ID zone ") + zone2);
998         }
999 
1000         if (tz1 && tz2) {
1001             // make NAME same so comparison will only look at the rest
1002             tz2->setID(tz1->getID(itsID));
1003 
1004             if (*tz1 != *tz2) {
1005                 errln("FAIL: " + UnicodeString(zone1) +
1006                       " != " + UnicodeString(zone2));
1007             } else {
1008                 logln("OK: " + UnicodeString(zone1) +
1009                       " == " + UnicodeString(zone2));
1010             }
1011         }
1012 
1013         delete tz1;
1014         delete tz2;
1015     }
1016 }
1017 
1018 
1019 /**
1020  * Utility function for TestCustomParse
1021  */
formatOffset(int32_t offset,UnicodeString & rv)1022 UnicodeString& TimeZoneTest::formatOffset(int32_t offset, UnicodeString &rv) {
1023     rv.remove();
1024     UChar sign = 0x002B;
1025     if (offset < 0) {
1026         sign = 0x002D;
1027         offset = -offset;
1028     }
1029 
1030     int32_t s = offset % 60;
1031     offset /= 60;
1032     int32_t m = offset % 60;
1033     int32_t h = offset / 60;
1034 
1035     rv += (UChar)(sign);
1036     if (h >= 10) {
1037         rv += (UChar)(0x0030 + (h/10));
1038     } else {
1039         rv += (UChar)0x0030;
1040     }
1041     rv += (UChar)(0x0030 + (h%10));
1042 
1043     rv += (UChar)0x003A; /* ':' */
1044     if (m >= 10) {
1045         rv += (UChar)(0x0030 + (m/10));
1046     } else {
1047         rv += (UChar)0x0030;
1048     }
1049     rv += (UChar)(0x0030 + (m%10));
1050 
1051     if (s) {
1052         rv += (UChar)0x003A; /* ':' */
1053         if (s >= 10) {
1054             rv += (UChar)(0x0030 + (s/10));
1055         } else {
1056             rv += (UChar)0x0030;
1057         }
1058         rv += (UChar)(0x0030 + (s%10));
1059     }
1060     return rv;
1061 }
1062 
1063 /**
1064  * Utility function for TestCustomParse, generating time zone ID
1065  * string for the give offset.
1066  */
formatTZID(int32_t offset,UnicodeString & rv)1067 UnicodeString& TimeZoneTest::formatTZID(int32_t offset, UnicodeString &rv) {
1068     rv.remove();
1069     UChar sign = 0x002B;
1070     if (offset < 0) {
1071         sign = 0x002D;
1072         offset = -offset;
1073     }
1074 
1075     int32_t s = offset % 60;
1076     offset /= 60;
1077     int32_t m = offset % 60;
1078     int32_t h = offset / 60;
1079 
1080     rv += "GMT";
1081     rv += (UChar)(sign);
1082     if (h >= 10) {
1083         rv += (UChar)(0x0030 + (h/10));
1084     } else {
1085         rv += (UChar)0x0030;
1086     }
1087     rv += (UChar)(0x0030 + (h%10));
1088     rv += (UChar)0x003A;
1089     if (m >= 10) {
1090         rv += (UChar)(0x0030 + (m/10));
1091     } else {
1092         rv += (UChar)0x0030;
1093     }
1094     rv += (UChar)(0x0030 + (m%10));
1095 
1096     if (s) {
1097         rv += (UChar)0x003A;
1098         if (s >= 10) {
1099             rv += (UChar)(0x0030 + (s/10));
1100         } else {
1101             rv += (UChar)0x0030;
1102         }
1103         rv += (UChar)(0x0030 + (s%10));
1104     }
1105     return rv;
1106 }
1107 
1108 /**
1109  * As part of the VM fix (see CCC approved RFE 4028006, bug
1110  * 4044013), TimeZone.getTimeZone() has been modified to recognize
1111  * generic IDs of the form GMT[+-]hh:mm, GMT[+-]hhmm, and
1112  * GMT[+-]hh.  Test this behavior here.
1113  *
1114  * @bug 4044013
1115  */
TestCustomParse()1116 void TimeZoneTest::TestCustomParse()
1117 {
1118     int32_t i;
1119     const int32_t kUnparseable = 604800; // the number of seconds in a week. More than any offset should be.
1120 
1121     struct
1122     {
1123         const char *customId;
1124         int32_t expectedOffset;
1125     }
1126     kData[] =
1127     {
1128         // ID        Expected offset in seconds
1129         {"GMT",       kUnparseable},   //Isn't custom. [returns normal GMT]
1130         {"GMT-YOUR.AD.HERE", kUnparseable},
1131         {"GMT0",      kUnparseable},
1132         {"GMT+0",     (0)},
1133         {"GMT+1",     (1*60*60)},
1134         {"GMT-0030",  (-30*60)},
1135         {"GMT+15:99", kUnparseable},
1136         {"GMT+",      kUnparseable},
1137         {"GMT-",      kUnparseable},
1138         {"GMT+0:",    kUnparseable},
1139         {"GMT-:",     kUnparseable},
1140         {"GMT-YOUR.AD.HERE",    kUnparseable},
1141         {"GMT+0010",  (10*60)}, // Interpret this as 00:10
1142         {"GMT-10",    (-10*60*60)},
1143         {"GMT+30",    kUnparseable},
1144         {"GMT-3:30",  (-(3*60+30)*60)},
1145         {"GMT-230",   (-(2*60+30)*60)},
1146         {"GMT+05:13:05",    ((5*60+13)*60+5)},
1147         {"GMT-71023",       (-((7*60+10)*60+23))},
1148         {"GMT+01:23:45:67", kUnparseable},
1149         {"GMT+01:234",      kUnparseable},
1150         {"GMT-2:31:123",    kUnparseable},
1151         {"GMT+3:75",        kUnparseable},
1152         {"GMT-01010101",    kUnparseable},
1153         {0,           0}
1154     };
1155 
1156     for (i=0; kData[i].customId != 0; i++) {
1157         UnicodeString id(kData[i].customId);
1158         int32_t exp = kData[i].expectedOffset;
1159         TimeZone *zone = TimeZone::createTimeZone(id);
1160         UnicodeString   itsID, temp;
1161 
1162         if (dynamic_cast<OlsonTimeZone *>(zone) != NULL) {
1163             logln(id + " -> Olson time zone");
1164         } else {
1165             zone->getID(itsID);
1166             int32_t ioffset = zone->getRawOffset()/1000;
1167             UnicodeString offset, expectedID;
1168             formatOffset(ioffset, offset);
1169             formatTZID(ioffset, expectedID);
1170             logln(id + " -> " + itsID + " " + offset);
1171             if (exp == kUnparseable && itsID != UCAL_UNKNOWN_ZONE_ID) {
1172                 errln("Expected parse failure for " + id +
1173                       ", got offset of " + offset +
1174                       ", id " + itsID);
1175             }
1176             // JDK 1.3 creates custom zones with the ID "Custom"
1177             // JDK 1.4 creates custom zones with IDs of the form "GMT+02:00"
1178             // ICU creates custom zones with IDs of the form "GMT+02:00"
1179             else if (exp != kUnparseable && (ioffset != exp || itsID != expectedID)) {
1180                 dataerrln("Expected offset of " + formatOffset(exp, temp) +
1181                       ", id " + expectedID +
1182                       ", for " + id +
1183                       ", got offset of " + offset +
1184                       ", id " + itsID);
1185             }
1186         }
1187         delete zone;
1188     }
1189 }
1190 
1191 void
TestAliasedNames()1192 TimeZoneTest::TestAliasedNames()
1193 {
1194     struct {
1195         const char *from;
1196         const char *to;
1197     } kData[] = {
1198         /* Generated by org.unicode.cldr.tool.CountItems */
1199 
1200         /* zoneID, canonical zoneID */
1201         {"Africa/Timbuktu", "Africa/Bamako"},
1202         {"America/Argentina/Buenos_Aires", "America/Buenos_Aires"},
1203         {"America/Argentina/Catamarca", "America/Catamarca"},
1204         {"America/Argentina/ComodRivadavia", "America/Catamarca"},
1205         {"America/Argentina/Cordoba", "America/Cordoba"},
1206         {"America/Argentina/Jujuy", "America/Jujuy"},
1207         {"America/Argentina/Mendoza", "America/Mendoza"},
1208         {"America/Atka", "America/Adak"},
1209         {"America/Ensenada", "America/Tijuana"},
1210         {"America/Fort_Wayne", "America/Indiana/Indianapolis"},
1211         {"America/Indianapolis", "America/Indiana/Indianapolis"},
1212         {"America/Knox_IN", "America/Indiana/Knox"},
1213         {"America/Louisville", "America/Kentucky/Louisville"},
1214         {"America/Porto_Acre", "America/Rio_Branco"},
1215         {"America/Rosario", "America/Cordoba"},
1216         {"America/Virgin", "America/St_Thomas"},
1217         {"Asia/Ashkhabad", "Asia/Ashgabat"},
1218         {"Asia/Chungking", "Asia/Chongqing"},
1219         {"Asia/Dacca", "Asia/Dhaka"},
1220         {"Asia/Istanbul", "Europe/Istanbul"},
1221         {"Asia/Macao", "Asia/Macau"},
1222         {"Asia/Tel_Aviv", "Asia/Jerusalem"},
1223         {"Asia/Thimbu", "Asia/Thimphu"},
1224         {"Asia/Ujung_Pandang", "Asia/Makassar"},
1225         {"Asia/Ulan_Bator", "Asia/Ulaanbaatar"},
1226         {"Australia/ACT", "Australia/Sydney"},
1227         {"Australia/Canberra", "Australia/Sydney"},
1228         {"Australia/LHI", "Australia/Lord_Howe"},
1229         {"Australia/NSW", "Australia/Sydney"},
1230         {"Australia/North", "Australia/Darwin"},
1231         {"Australia/Queensland", "Australia/Brisbane"},
1232         {"Australia/South", "Australia/Adelaide"},
1233         {"Australia/Tasmania", "Australia/Hobart"},
1234         {"Australia/Victoria", "Australia/Melbourne"},
1235         {"Australia/West", "Australia/Perth"},
1236         {"Australia/Yancowinna", "Australia/Broken_Hill"},
1237         {"Brazil/Acre", "America/Rio_Branco"},
1238         {"Brazil/DeNoronha", "America/Noronha"},
1239         {"Brazil/East", "America/Sao_Paulo"},
1240         {"Brazil/West", "America/Manaus"},
1241         {"Canada/Atlantic", "America/Halifax"},
1242         {"Canada/Central", "America/Winnipeg"},
1243         {"Canada/East-Saskatchewan", "America/Regina"},
1244         {"Canada/Eastern", "America/Toronto"},
1245         {"Canada/Mountain", "America/Edmonton"},
1246         {"Canada/Newfoundland", "America/St_Johns"},
1247         {"Canada/Pacific", "America/Vancouver"},
1248         {"Canada/Saskatchewan", "America/Regina"},
1249         {"Canada/Yukon", "America/Whitehorse"},
1250         {"Chile/Continental", "America/Santiago"},
1251         {"Chile/EasterIsland", "Pacific/Easter"},
1252         {"Cuba", "America/Havana"},
1253         {"Egypt", "Africa/Cairo"},
1254         {"Eire", "Europe/Dublin"},
1255         {"Etc/GMT+0", "Etc/GMT"},
1256         {"Etc/GMT-0", "Etc/GMT"},
1257         {"Etc/GMT0", "Etc/GMT"},
1258         {"Etc/Greenwich", "Etc/GMT"},
1259         {"Etc/UCT", "Etc/GMT"},
1260         {"Etc/UTC", "Etc/GMT"},
1261         {"Etc/Universal", "Etc/GMT"},
1262         {"Etc/Zulu", "Etc/GMT"},
1263         {"Europe/Belfast", "Europe/London"},
1264         {"Europe/Nicosia", "Asia/Nicosia"},
1265         {"Europe/Tiraspol", "Europe/Chisinau"},
1266         {"GB", "Europe/London"},
1267         {"GB-Eire", "Europe/London"},
1268         {"GMT", "Etc/GMT"},
1269         {"GMT+0", "Etc/GMT"},
1270         {"GMT-0", "Etc/GMT"},
1271         {"GMT0", "Etc/GMT"},
1272         {"Greenwich", "Etc/GMT"},
1273         {"Hongkong", "Asia/Hong_Kong"},
1274         {"Iceland", "Atlantic/Reykjavik"},
1275         {"Iran", "Asia/Tehran"},
1276         {"Israel", "Asia/Jerusalem"},
1277         {"Jamaica", "America/Jamaica"},
1278         {"Japan", "Asia/Tokyo"},
1279         {"Kwajalein", "Pacific/Kwajalein"},
1280         {"Libya", "Africa/Tripoli"},
1281         {"Mexico/BajaNorte", "America/Tijuana"},
1282         {"Mexico/BajaSur", "America/Mazatlan"},
1283         {"Mexico/General", "America/Mexico_City"},
1284         {"NZ", "Pacific/Auckland"},
1285         {"NZ-CHAT", "Pacific/Chatham"},
1286         {"Navajo", "America/Shiprock"},
1287         {"PRC", "Asia/Shanghai"},
1288         {"Pacific/Samoa", "Pacific/Pago_Pago"},
1289         {"Pacific/Yap", "Pacific/Truk"},
1290         {"Poland", "Europe/Warsaw"},
1291         {"Portugal", "Europe/Lisbon"},
1292         {"ROC", "Asia/Taipei"},
1293         {"ROK", "Asia/Seoul"},
1294         {"Singapore", "Asia/Singapore"},
1295         {"Turkey", "Europe/Istanbul"},
1296         {"UCT", "Etc/GMT"},
1297         {"US/Alaska", "America/Anchorage"},
1298         {"US/Aleutian", "America/Adak"},
1299         {"US/Arizona", "America/Phoenix"},
1300         {"US/Central", "America/Chicago"},
1301         {"US/East-Indiana", "America/Indiana/Indianapolis"},
1302         {"US/Eastern", "America/New_York"},
1303         {"US/Hawaii", "Pacific/Honolulu"},
1304         {"US/Indiana-Starke", "America/Indiana/Knox"},
1305         {"US/Michigan", "America/Detroit"},
1306         {"US/Mountain", "America/Denver"},
1307         {"US/Pacific", "America/Los_Angeles"},
1308         {"US/Pacific-New", "America/Los_Angeles"},
1309         {"US/Samoa", "Pacific/Pago_Pago"},
1310         {"UTC", "Etc/GMT"},
1311         {"Universal", "Etc/GMT"},
1312         {"W-SU", "Europe/Moscow"},
1313         {"Zulu", "Etc/GMT"},
1314         /* Total: 113 */
1315 
1316     };
1317 
1318     TimeZone::EDisplayType styles[] = { TimeZone::SHORT, TimeZone::LONG };
1319     UBool useDst[] = { FALSE, TRUE };
1320     int32_t noLoc = uloc_countAvailable();
1321 
1322     int32_t i, j, k, loc;
1323     UnicodeString fromName, toName;
1324     TimeZone *from = NULL, *to = NULL;
1325     for(i = 0; i < (int32_t)(sizeof(kData)/sizeof(kData[0])); i++) {
1326         from = TimeZone::createTimeZone(kData[i].from);
1327         to = TimeZone::createTimeZone(kData[i].to);
1328         if(!from->hasSameRules(*to)) {
1329             errln("different at %i\n", i);
1330         }
1331         if(!quick) {
1332             for(loc = 0; loc < noLoc; loc++) {
1333                 const char* locale = uloc_getAvailable(loc);
1334                 for(j = 0; j < (int32_t)(sizeof(styles)/sizeof(styles[0])); j++) {
1335                     for(k = 0; k < (int32_t)(sizeof(useDst)/sizeof(useDst[0])); k++) {
1336                         fromName.remove();
1337                         toName.remove();
1338                         from->getDisplayName(useDst[k], styles[j],locale, fromName);
1339                         to->getDisplayName(useDst[k], styles[j], locale, toName);
1340                         if(fromName.compare(toName) != 0) {
1341                             errln("Fail: Expected "+toName+" but got " + prettify(fromName)
1342                                 + " for locale: " + locale + " index: "+ loc
1343                                 + " to id "+ kData[i].to
1344                                 + " from id " + kData[i].from);
1345                         }
1346                     }
1347                 }
1348             }
1349         } else {
1350             fromName.remove();
1351             toName.remove();
1352             from->getDisplayName(fromName);
1353             to->getDisplayName(toName);
1354             if(fromName.compare(toName) != 0) {
1355                 errln("Fail: Expected "+toName+" but got " + fromName);
1356             }
1357         }
1358         delete from;
1359         delete to;
1360     }
1361 }
1362 
1363 /**
1364  * Test the basic functionality of the getDisplayName() API.
1365  *
1366  * @bug 4112869
1367  * @bug 4028006
1368  *
1369  * See also API change request A41.
1370  *
1371  * 4/21/98 - make smarter, so the test works if the ext resources
1372  * are present or not.
1373  */
1374 void
TestDisplayName()1375 TimeZoneTest::TestDisplayName()
1376 {
1377     UErrorCode status = U_ZERO_ERROR;
1378     int32_t i;
1379     TimeZone *zone = TimeZone::createTimeZone("PST");
1380     UnicodeString name;
1381     zone->getDisplayName(Locale::getEnglish(), name);
1382     logln("PST->" + name);
1383     if (name.compare("Pacific Standard Time") != 0)
1384         dataerrln("Fail: Expected \"Pacific Standard Time\" but got " + name);
1385 
1386     //*****************************************************************
1387     // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES
1388     // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES
1389     // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES
1390     //*****************************************************************
1391     struct
1392     {
1393         UBool useDst;
1394         TimeZone::EDisplayType style;
1395         const char *expect;
1396     } kData[] = {
1397         {FALSE, TimeZone::SHORT, "PST"},
1398         {TRUE,  TimeZone::SHORT, "PDT"},
1399         {FALSE, TimeZone::LONG,  "Pacific Standard Time"},
1400         {TRUE,  TimeZone::LONG,  "Pacific Daylight Time"},
1401 
1402         {FALSE, TimeZone::SHORT_GENERIC, "PT"},
1403         {TRUE,  TimeZone::SHORT_GENERIC, "PT"},
1404         {FALSE, TimeZone::LONG_GENERIC,  "Pacific Time"},
1405         {TRUE,  TimeZone::LONG_GENERIC,  "Pacific Time"},
1406 
1407         {FALSE, TimeZone::SHORT_GMT, "-0800"},
1408         {TRUE,  TimeZone::SHORT_GMT, "-0700"},
1409         {FALSE, TimeZone::LONG_GMT,  "GMT-08:00"},
1410         {TRUE,  TimeZone::LONG_GMT,  "GMT-07:00"},
1411 
1412         {FALSE, TimeZone::SHORT_COMMONLY_USED, "PST"},
1413         {TRUE,  TimeZone::SHORT_COMMONLY_USED, "PDT"},
1414         {FALSE, TimeZone::GENERIC_LOCATION,  "Los Angeles Time"},
1415         {TRUE,  TimeZone::GENERIC_LOCATION,  "Los Angeles Time"},
1416 
1417         {FALSE, TimeZone::LONG, ""}
1418     };
1419 
1420     for (i=0; kData[i].expect[0] != '\0'; i++)
1421     {
1422         name.remove();
1423         name = zone->getDisplayName(kData[i].useDst,
1424                                    kData[i].style,
1425                                    Locale::getEnglish(), name);
1426         if (name.compare(kData[i].expect) != 0)
1427             dataerrln("Fail: Expected " + UnicodeString(kData[i].expect) + "; got " + name);
1428         logln("PST [with options]->" + name);
1429     }
1430     for (i=0; kData[i].expect[0] != '\0'; i++)
1431     {
1432         name.remove();
1433         name = zone->getDisplayName(kData[i].useDst,
1434                                    kData[i].style, name);
1435         if (name.compare(kData[i].expect) != 0)
1436             dataerrln("Fail: Expected " + UnicodeString(kData[i].expect) + "; got " + name);
1437         logln("PST [with options]->" + name);
1438     }
1439 
1440 
1441     // Make sure that we don't display the DST name by constructing a fake
1442     // PST zone that has DST all year long.
1443     SimpleTimeZone *zone2 = new SimpleTimeZone(0, "PST");
1444 
1445     zone2->setStartRule(UCAL_JANUARY, 1, 0, 0, status);
1446     zone2->setEndRule(UCAL_DECEMBER, 31, 0, 0, status);
1447 
1448     UnicodeString inDaylight;
1449     if (zone2->inDaylightTime(UDate(0), status)) {
1450         inDaylight = UnicodeString("TRUE");
1451     } else {
1452         inDaylight = UnicodeString("FALSE");
1453     }
1454     logln(UnicodeString("Modified PST inDaylightTime->") + inDaylight );
1455     if(U_FAILURE(status))
1456     {
1457         dataerrln("Some sort of error..." + UnicodeString(u_errorName(status))); // REVISIT
1458     }
1459     name.remove();
1460     name = zone2->getDisplayName(Locale::getEnglish(),name);
1461     logln("Modified PST->" + name);
1462     if (name.compare("Pacific Standard Time") != 0)
1463         dataerrln("Fail: Expected \"Pacific Standard Time\"");
1464 
1465     // Make sure we get the default display format for Locales
1466     // with no display name data.
1467     Locale mt_MT("mt_MT");
1468     name.remove();
1469     name = zone->getDisplayName(mt_MT,name);
1470     //*****************************************************************
1471     // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES
1472     // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES
1473     // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES
1474     //*****************************************************************
1475     logln("PST(mt_MT)->" + name);
1476 
1477     // *** REVISIT SRL how in the world do I check this? looks java specific.
1478     // Now be smart -- check to see if zh resource is even present.
1479     // If not, we expect the en fallback behavior.
1480     ResourceBundle enRB(NULL,
1481                             Locale::getEnglish(), status);
1482     if(U_FAILURE(status))
1483         dataerrln("Couldn't get ResourceBundle for en - %s", u_errorName(status));
1484 
1485     ResourceBundle mtRB(NULL,
1486                          mt_MT, status);
1487     //if(U_FAILURE(status))
1488     //    errln("Couldn't get ResourceBundle for mt_MT");
1489 
1490     UBool noZH = U_FAILURE(status);
1491 
1492     if (noZH) {
1493         logln("Warning: Not testing the mt_MT behavior because resource is absent");
1494         if (name != "Pacific Standard Time")
1495             dataerrln("Fail: Expected Pacific Standard Time");
1496     }
1497 
1498 
1499     if      (name.compare("GMT-08:00") &&
1500              name.compare("GMT-8:00") &&
1501              name.compare("GMT-0800") &&
1502              name.compare("GMT-800")) {
1503       dataerrln(UnicodeString("Fail: Expected GMT-08:00 or something similar for PST in mt_MT but got ") + name );
1504         dataerrln("************************************************************");
1505         dataerrln("THE ABOVE FAILURE MAY JUST MEAN THE LOCALE DATA HAS CHANGED");
1506         dataerrln("************************************************************");
1507     }
1508 
1509     // Now try a non-existent zone
1510     delete zone2;
1511     zone2 = new SimpleTimeZone(90*60*1000, "xyzzy");
1512     name.remove();
1513     name = zone2->getDisplayName(Locale::getEnglish(),name);
1514     logln("GMT+90min->" + name);
1515     if (name.compare("GMT+01:30") &&
1516         name.compare("GMT+1:30") &&
1517         name.compare("GMT+0130") &&
1518         name.compare("GMT+130"))
1519         dataerrln("Fail: Expected GMT+01:30 or something similar");
1520     name.truncate(0);
1521     zone2->getDisplayName(name);
1522     logln("GMT+90min->" + name);
1523     if (name.compare("GMT+01:30") &&
1524         name.compare("GMT+1:30") &&
1525         name.compare("GMT+0130") &&
1526         name.compare("GMT+130"))
1527         dataerrln("Fail: Expected GMT+01:30 or something similar");
1528     // clean up
1529     delete zone;
1530     delete zone2;
1531 }
1532 
1533 /**
1534  * @bug 4107276
1535  */
1536 void
TestDSTSavings()1537 TimeZoneTest::TestDSTSavings()
1538 {
1539     UErrorCode status = U_ZERO_ERROR;
1540     // It might be better to find a way to integrate this test into the main TimeZone
1541     // tests above, but I don't have time to figure out how to do this (or if it's
1542     // even really a good idea).  Let's consider that a future.  --rtg 1/27/98
1543     SimpleTimeZone *tz = new SimpleTimeZone(-5 * U_MILLIS_PER_HOUR, "dstSavingsTest",
1544                                            UCAL_MARCH, 1, 0, 0, UCAL_SEPTEMBER, 1, 0, 0,
1545                                            (int32_t)(0.5 * U_MILLIS_PER_HOUR), status);
1546     if(U_FAILURE(status))
1547         errln("couldn't create TimeZone");
1548 
1549     if (tz->getRawOffset() != -5 * U_MILLIS_PER_HOUR)
1550         errln(UnicodeString("Got back a raw offset of ") + (tz->getRawOffset() / U_MILLIS_PER_HOUR) +
1551               " hours instead of -5 hours.");
1552     if (!tz->useDaylightTime())
1553         errln("Test time zone should use DST but claims it doesn't.");
1554     if (tz->getDSTSavings() != 0.5 * U_MILLIS_PER_HOUR)
1555         errln(UnicodeString("Set DST offset to 0.5 hour, but got back ") + (tz->getDSTSavings() /
1556                                                              U_MILLIS_PER_HOUR) + " hours instead.");
1557 
1558     int32_t offset = tz->getOffset(GregorianCalendar::AD, 1998, UCAL_JANUARY, 1,
1559                               UCAL_THURSDAY, 10 * U_MILLIS_PER_HOUR,status);
1560     if (offset != -5 * U_MILLIS_PER_HOUR)
1561         errln(UnicodeString("The offset for 10 AM, 1/1/98 should have been -5 hours, but we got ")
1562               + (offset / U_MILLIS_PER_HOUR) + " hours.");
1563 
1564     offset = tz->getOffset(GregorianCalendar::AD, 1998, UCAL_JUNE, 1, UCAL_MONDAY,
1565                           10 * U_MILLIS_PER_HOUR,status);
1566     if (offset != -4.5 * U_MILLIS_PER_HOUR)
1567         errln(UnicodeString("The offset for 10 AM, 6/1/98 should have been -4.5 hours, but we got ")
1568               + (offset / U_MILLIS_PER_HOUR) + " hours.");
1569 
1570     tz->setDSTSavings(U_MILLIS_PER_HOUR, status);
1571     offset = tz->getOffset(GregorianCalendar::AD, 1998, UCAL_JANUARY, 1,
1572                           UCAL_THURSDAY, 10 * U_MILLIS_PER_HOUR,status);
1573     if (offset != -5 * U_MILLIS_PER_HOUR)
1574         errln(UnicodeString("The offset for 10 AM, 1/1/98 should have been -5 hours, but we got ")
1575               + (offset / U_MILLIS_PER_HOUR) + " hours.");
1576 
1577     offset = tz->getOffset(GregorianCalendar::AD, 1998, UCAL_JUNE, 1, UCAL_MONDAY,
1578                           10 * U_MILLIS_PER_HOUR,status);
1579     if (offset != -4 * U_MILLIS_PER_HOUR)
1580         errln(UnicodeString("The offset for 10 AM, 6/1/98 (with a 1-hour DST offset) should have been -4 hours, but we got ")
1581               + (offset / U_MILLIS_PER_HOUR) + " hours.");
1582 
1583     delete tz;
1584 }
1585 
1586 /**
1587  * @bug 4107570
1588  */
1589 void
TestAlternateRules()1590 TimeZoneTest::TestAlternateRules()
1591 {
1592     // Like TestDSTSavings, this test should probably be integrated somehow with the main
1593     // test at the top of this class, but I didn't have time to figure out how to do that.
1594     //                      --rtg 1/28/98
1595 
1596     SimpleTimeZone tz(-5 * U_MILLIS_PER_HOUR, "alternateRuleTest");
1597 
1598     // test the day-of-month API
1599     UErrorCode status = U_ZERO_ERROR;
1600     tz.setStartRule(UCAL_MARCH, 10, 12 * U_MILLIS_PER_HOUR, status);
1601     if(U_FAILURE(status))
1602         errln("tz.setStartRule failed");
1603     tz.setEndRule(UCAL_OCTOBER, 20, 12 * U_MILLIS_PER_HOUR, status);
1604     if(U_FAILURE(status))
1605         errln("tz.setStartRule failed");
1606 
1607     int32_t offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_MARCH, 5,
1608                               UCAL_THURSDAY, 10 * U_MILLIS_PER_HOUR,status);
1609     if (offset != -5 * U_MILLIS_PER_HOUR)
1610         errln(UnicodeString("The offset for 10AM, 3/5/98 should have been -5 hours, but we got ")
1611               + (offset / U_MILLIS_PER_HOUR) + " hours.");
1612 
1613     offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_MARCH, 15,
1614                           UCAL_SUNDAY, 10 * millisPerHour,status);
1615     if (offset != -4 * U_MILLIS_PER_HOUR)
1616         errln(UnicodeString("The offset for 10AM, 3/15/98 should have been -4 hours, but we got ")
1617               + (offset / U_MILLIS_PER_HOUR) + " hours.");
1618 
1619     offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_OCTOBER, 15,
1620                           UCAL_THURSDAY, 10 * millisPerHour,status);
1621     if (offset != -4 * U_MILLIS_PER_HOUR)
1622         errln(UnicodeString("The offset for 10AM, 10/15/98 should have been -4 hours, but we got ")              + (offset / U_MILLIS_PER_HOUR) + " hours.");
1623 
1624     offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_OCTOBER, 25,
1625                           UCAL_SUNDAY, 10 * millisPerHour,status);
1626     if (offset != -5 * U_MILLIS_PER_HOUR)
1627         errln(UnicodeString("The offset for 10AM, 10/25/98 should have been -5 hours, but we got ")
1628               + (offset / U_MILLIS_PER_HOUR) + " hours.");
1629 
1630     // test the day-of-week-after-day-in-month API
1631     tz.setStartRule(UCAL_MARCH, 10, UCAL_FRIDAY, 12 * millisPerHour, TRUE, status);
1632     if(U_FAILURE(status))
1633         errln("tz.setStartRule failed");
1634     tz.setEndRule(UCAL_OCTOBER, 20, UCAL_FRIDAY, 12 * millisPerHour, FALSE, status);
1635     if(U_FAILURE(status))
1636         errln("tz.setStartRule failed");
1637 
1638     offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_MARCH, 11,
1639                           UCAL_WEDNESDAY, 10 * millisPerHour,status);
1640     if (offset != -5 * U_MILLIS_PER_HOUR)
1641         errln(UnicodeString("The offset for 10AM, 3/11/98 should have been -5 hours, but we got ")
1642               + (offset / U_MILLIS_PER_HOUR) + " hours.");
1643 
1644     offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_MARCH, 14,
1645                           UCAL_SATURDAY, 10 * millisPerHour,status);
1646     if (offset != -4 * U_MILLIS_PER_HOUR)
1647         errln(UnicodeString("The offset for 10AM, 3/14/98 should have been -4 hours, but we got ")
1648               + (offset / U_MILLIS_PER_HOUR) + " hours.");
1649 
1650     offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_OCTOBER, 15,
1651                           UCAL_THURSDAY, 10 * millisPerHour,status);
1652     if (offset != -4 * U_MILLIS_PER_HOUR)
1653         errln(UnicodeString("The offset for 10AM, 10/15/98 should have been -4 hours, but we got ")
1654               + (offset / U_MILLIS_PER_HOUR) + " hours.");
1655 
1656     offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_OCTOBER, 17,
1657                           UCAL_SATURDAY, 10 * millisPerHour,status);
1658     if (offset != -5 * U_MILLIS_PER_HOUR)
1659         errln(UnicodeString("The offset for 10AM, 10/17/98 should have been -5 hours, but we got ")
1660               + (offset / U_MILLIS_PER_HOUR) + " hours.");
1661 }
1662 
TestFractionalDST()1663 void TimeZoneTest::TestFractionalDST() {
1664     const char* tzName = "Australia/Lord_Howe"; // 30 min offset
1665     TimeZone* tz_icu = TimeZone::createTimeZone(tzName);
1666 	int dst_icu = tz_icu->getDSTSavings();
1667     UnicodeString id;
1668     int32_t expected = 1800000;
1669 	if (expected != dst_icu) {
1670 	    dataerrln(UnicodeString("java reports dst savings of ") + expected +
1671 	        " but icu reports " + dst_icu +
1672 	        " for tz " + tz_icu->getID(id));
1673 	} else {
1674 	    logln(UnicodeString("both java and icu report dst savings of ") + expected + " for tz " + tz_icu->getID(id));
1675 	}
1676     delete tz_icu;
1677 }
1678 
1679 /**
1680  * Test country code support.  Jitterbug 776.
1681  */
TestCountries()1682 void TimeZoneTest::TestCountries() {
1683     // Make sure America/Los_Angeles is in the "US" group, and
1684     // Asia/Tokyo isn't.  Vice versa for the "JP" group.
1685     UErrorCode ec = U_ZERO_ERROR;
1686     int32_t n;
1687     StringEnumeration* s = TimeZone::createEnumeration("US");
1688     if (s == NULL) {
1689         dataerrln("Unable to create TimeZone enumeration for US");
1690         return;
1691     }
1692     n = s->count(ec);
1693     UBool la = FALSE, tokyo = FALSE;
1694     UnicodeString laZone("America/Los_Angeles", "");
1695     UnicodeString tokyoZone("Asia/Tokyo", "");
1696     int32_t i;
1697 
1698     if (s == NULL || n <= 0) {
1699         dataerrln("FAIL: TimeZone::createEnumeration() returned nothing");
1700         return;
1701     }
1702     for (i=0; i<n; ++i) {
1703         const UnicodeString* id = s->snext(ec);
1704         if (*id == (laZone)) {
1705             la = TRUE;
1706         }
1707         if (*id == (tokyoZone)) {
1708             tokyo = TRUE;
1709         }
1710     }
1711     if (!la || tokyo) {
1712         errln("FAIL: " + laZone + " in US = " + la);
1713         errln("FAIL: " + tokyoZone + " in US = " + tokyo);
1714     }
1715     delete s;
1716 
1717     s = TimeZone::createEnumeration("JP");
1718     n = s->count(ec);
1719     la = FALSE; tokyo = FALSE;
1720 
1721     for (i=0; i<n; ++i) {
1722         const UnicodeString* id = s->snext(ec);
1723         if (*id == (laZone)) {
1724             la = TRUE;
1725         }
1726         if (*id == (tokyoZone)) {
1727             tokyo = TRUE;
1728         }
1729     }
1730     if (la || !tokyo) {
1731         errln("FAIL: " + laZone + " in JP = " + la);
1732         errln("FAIL: " + tokyoZone + " in JP = " + tokyo);
1733     }
1734     StringEnumeration* s1 = TimeZone::createEnumeration("US");
1735     StringEnumeration* s2 = TimeZone::createEnumeration("US");
1736     for(i=0;i<n;++i){
1737         const UnicodeString* id1 = s1->snext(ec);
1738         if(id1==NULL || U_FAILURE(ec)){
1739             errln("Failed to fetch next from TimeZone enumeration. Length returned : %i Current Index: %i", n,i);
1740         }
1741         TimeZone* tz1 = TimeZone::createTimeZone(*id1);
1742         for(int j=0; j<n;++j){
1743             const UnicodeString* id2 = s2->snext(ec);
1744             if(id2==NULL || U_FAILURE(ec)){
1745                 errln("Failed to fetch next from TimeZone enumeration. Length returned : %i Current Index: %i", n,i);
1746             }
1747             TimeZone* tz2 = TimeZone::createTimeZone(*id2);
1748             if(tz1->hasSameRules(*tz2)){
1749                 logln("ID1 : " + *id1+" == ID2 : " +*id2);
1750             }
1751             delete tz2;
1752         }
1753         delete tz1;
1754     }
1755     delete s1;
1756     delete s2;
1757     delete s;
1758 }
1759 
TestHistorical()1760 void TimeZoneTest::TestHistorical() {
1761     const int32_t H = U_MILLIS_PER_HOUR;
1762     struct {
1763         const char* id;
1764         int32_t time; // epoch seconds
1765         int32_t offset; // total offset (millis)
1766     } DATA[] = {
1767         // Add transition points (before/after) as desired to test historical
1768         // behavior.
1769         {"America/Los_Angeles", 638963999, -8*H}, // Sun Apr 01 01:59:59 GMT-08:00 1990
1770         {"America/Los_Angeles", 638964000, -7*H}, // Sun Apr 01 03:00:00 GMT-07:00 1990
1771         {"America/Los_Angeles", 657104399, -7*H}, // Sun Oct 28 01:59:59 GMT-07:00 1990
1772         {"America/Los_Angeles", 657104400, -8*H}, // Sun Oct 28 01:00:00 GMT-08:00 1990
1773         {"America/Goose_Bay", -116445601, -4*H}, // Sun Apr 24 01:59:59 GMT-04:00 1966
1774         {"America/Goose_Bay", -116445600, -3*H}, // Sun Apr 24 03:00:00 GMT-03:00 1966
1775         {"America/Goose_Bay", -100119601, -3*H}, // Sun Oct 30 01:59:59 GMT-03:00 1966
1776         {"America/Goose_Bay", -100119600, -4*H}, // Sun Oct 30 01:00:00 GMT-04:00 1966
1777         {"America/Goose_Bay", -84391201, -4*H}, // Sun Apr 30 01:59:59 GMT-04:00 1967
1778         {"America/Goose_Bay", -84391200, -3*H}, // Sun Apr 30 03:00:00 GMT-03:00 1967
1779         {"America/Goose_Bay", -68670001, -3*H}, // Sun Oct 29 01:59:59 GMT-03:00 1967
1780         {"America/Goose_Bay", -68670000, -4*H}, // Sun Oct 29 01:00:00 GMT-04:00 1967
1781         {0, 0, 0}
1782     };
1783 
1784     for (int32_t i=0; DATA[i].id!=0; ++i) {
1785         const char* id = DATA[i].id;
1786         TimeZone *tz = TimeZone::createTimeZone(id);
1787         UnicodeString s;
1788         if (tz == 0) {
1789             errln("FAIL: Cannot create %s", id);
1790         } else if (tz->getID(s) != UnicodeString(id)) {
1791             dataerrln((UnicodeString)"FAIL: createTimeZone(" + id + ") => " + s);
1792         } else {
1793             UErrorCode ec = U_ZERO_ERROR;
1794             int32_t raw, dst;
1795             UDate when = (double) DATA[i].time * U_MILLIS_PER_SECOND;
1796             tz->getOffset(when, FALSE, raw, dst, ec);
1797             if (U_FAILURE(ec)) {
1798                 errln("FAIL: getOffset");
1799             } else if ((raw+dst) != DATA[i].offset) {
1800                 errln((UnicodeString)"FAIL: " + DATA[i].id + ".getOffset(" +
1801                       //when + " = " +
1802                       dateToString(when) + ") => " +
1803                       raw + ", " + dst);
1804             } else {
1805                 logln((UnicodeString)"Ok: " + DATA[i].id + ".getOffset(" +
1806                       //when + " = " +
1807                       dateToString(when) + ") => " +
1808                       raw + ", " + dst);
1809             }
1810         }
1811         delete tz;
1812     }
1813 }
1814 
TestEquivalentIDs()1815 void TimeZoneTest::TestEquivalentIDs() {
1816     int32_t n = TimeZone::countEquivalentIDs("PST");
1817     if (n < 2) {
1818         dataerrln((UnicodeString)"FAIL: countEquivalentIDs(PST) = " + n);
1819     } else {
1820         UBool sawLA = FALSE;
1821         for (int32_t i=0; i<n; ++i) {
1822             UnicodeString id = TimeZone::getEquivalentID("PST", i);
1823             logln((UnicodeString)"" + i + " : " + id);
1824             if (id == UnicodeString("America/Los_Angeles")) {
1825                 sawLA = TRUE;
1826             }
1827         }
1828         if (!sawLA) {
1829             errln("FAIL: America/Los_Angeles should be in the list");
1830         }
1831     }
1832 }
1833 
1834 // Test that a transition at the end of February is handled correctly.
TestFebruary()1835 void TimeZoneTest::TestFebruary() {
1836     UErrorCode status = U_ZERO_ERROR;
1837 
1838     // Time zone with daylight savings time from the first Sunday in November
1839     // to the last Sunday in February.
1840     // Similar to the new rule for Brazil (Sao Paulo) in tzdata2006n.
1841     //
1842     // Note: In tzdata2007h, the rule had changed, so no actual zones uses
1843     // lastSun in Feb anymore.
1844     SimpleTimeZone tz1(-3 * U_MILLIS_PER_HOUR,          // raw offset: 3h before (west of) GMT
1845                        UNICODE_STRING("nov-feb", 7),
1846                        UCAL_NOVEMBER, 1, UCAL_SUNDAY,   // start: November, first, Sunday
1847                        0,                               //        midnight wall time
1848                        UCAL_FEBRUARY, -1, UCAL_SUNDAY,  // end:   February, last, Sunday
1849                        0,                               //        midnight wall time
1850                        status);
1851     if (U_FAILURE(status)) {
1852         errln("Unable to create the SimpleTimeZone(nov-feb): %s", u_errorName(status));
1853         return;
1854     }
1855 
1856     // Now hardcode the same rules as for Brazil in tzdata 2006n, so that
1857     // we cover the intended code even when in the future zoneinfo hardcodes
1858     // these transition dates.
1859     SimpleTimeZone tz2(-3 * U_MILLIS_PER_HOUR,          // raw offset: 3h before (west of) GMT
1860                        UNICODE_STRING("nov-feb2", 8),
1861                        UCAL_NOVEMBER, 1, -UCAL_SUNDAY,  // start: November, 1 or after, Sunday
1862                        0,                               //        midnight wall time
1863                        UCAL_FEBRUARY, -29, -UCAL_SUNDAY,// end:   February, 29 or before, Sunday
1864                        0,                               //        midnight wall time
1865                        status);
1866     if (U_FAILURE(status)) {
1867         errln("Unable to create the SimpleTimeZone(nov-feb2): %s", u_errorName(status));
1868         return;
1869     }
1870 
1871     // Gregorian calendar with the UTC time zone for getting sample test date/times.
1872     GregorianCalendar gc(*TimeZone::getGMT(), status);
1873     if (U_FAILURE(status)) {
1874         dataerrln("Unable to create the UTC calendar: %s", u_errorName(status));
1875         return;
1876     }
1877 
1878     struct {
1879         // UTC time.
1880         int32_t year, month, day, hour, minute, second;
1881         // Expected time zone offset in hours after GMT (negative=before GMT).
1882         int32_t offsetHours;
1883     } data[] = {
1884         { 2006, UCAL_NOVEMBER,  5, 02, 59, 59, -3 },
1885         { 2006, UCAL_NOVEMBER,  5, 03, 00, 00, -2 },
1886         { 2007, UCAL_FEBRUARY, 25, 01, 59, 59, -2 },
1887         { 2007, UCAL_FEBRUARY, 25, 02, 00, 00, -3 },
1888 
1889         { 2007, UCAL_NOVEMBER,  4, 02, 59, 59, -3 },
1890         { 2007, UCAL_NOVEMBER,  4, 03, 00, 00, -2 },
1891         { 2008, UCAL_FEBRUARY, 24, 01, 59, 59, -2 },
1892         { 2008, UCAL_FEBRUARY, 24, 02, 00, 00, -3 },
1893 
1894         { 2008, UCAL_NOVEMBER,  2, 02, 59, 59, -3 },
1895         { 2008, UCAL_NOVEMBER,  2, 03, 00, 00, -2 },
1896         { 2009, UCAL_FEBRUARY, 22, 01, 59, 59, -2 },
1897         { 2009, UCAL_FEBRUARY, 22, 02, 00, 00, -3 },
1898 
1899         { 2009, UCAL_NOVEMBER,  1, 02, 59, 59, -3 },
1900         { 2009, UCAL_NOVEMBER,  1, 03, 00, 00, -2 },
1901         { 2010, UCAL_FEBRUARY, 28, 01, 59, 59, -2 },
1902         { 2010, UCAL_FEBRUARY, 28, 02, 00, 00, -3 }
1903     };
1904 
1905     TimeZone *timezones[] = { &tz1, &tz2 };
1906 
1907     TimeZone *tz;
1908     UDate dt;
1909     int32_t t, i, raw, dst;
1910     for (t = 0; t < LENGTHOF(timezones); ++t) {
1911         tz = timezones[t];
1912         for (i = 0; i < LENGTHOF(data); ++i) {
1913             gc.set(data[i].year, data[i].month, data[i].day,
1914                    data[i].hour, data[i].minute, data[i].second);
1915             dt = gc.getTime(status);
1916             if (U_FAILURE(status)) {
1917                 errln("test case %d.%d: bad date/time %04d-%02d-%02d %02d:%02d:%02d",
1918                       t, i,
1919                       data[i].year, data[i].month + 1, data[i].day,
1920                       data[i].hour, data[i].minute, data[i].second);
1921                 status = U_ZERO_ERROR;
1922                 continue;
1923             }
1924             tz->getOffset(dt, FALSE, raw, dst, status);
1925             if (U_FAILURE(status)) {
1926                 errln("test case %d.%d: tz.getOffset(%04d-%02d-%02d %02d:%02d:%02d) fails: %s",
1927                       t, i,
1928                       data[i].year, data[i].month + 1, data[i].day,
1929                       data[i].hour, data[i].minute, data[i].second,
1930                       u_errorName(status));
1931                 status = U_ZERO_ERROR;
1932             } else if ((raw + dst) != data[i].offsetHours * U_MILLIS_PER_HOUR) {
1933                 errln("test case %d.%d: tz.getOffset(%04d-%02d-%02d %02d:%02d:%02d) returns %d+%d != %d",
1934                       t, i,
1935                       data[i].year, data[i].month + 1, data[i].day,
1936                       data[i].hour, data[i].minute, data[i].second,
1937                       raw, dst, data[i].offsetHours * U_MILLIS_PER_HOUR);
1938             }
1939         }
1940     }
1941 }
1942 
TestCanonicalIDAPI()1943 void TimeZoneTest::TestCanonicalIDAPI() {
1944     // Bogus input string.
1945     UnicodeString bogus;
1946     bogus.setToBogus();
1947     UnicodeString canonicalID;
1948     UErrorCode ec = U_ZERO_ERROR;
1949     UnicodeString *pResult = &TimeZone::getCanonicalID(bogus, canonicalID, ec);
1950     assertEquals("TimeZone::getCanonicalID(bogus) should fail", U_ILLEGAL_ARGUMENT_ERROR, ec);
1951     assertTrue("TimeZone::getCanonicalID(bogus) should return the dest string", pResult == &canonicalID);
1952 
1953     // U_FAILURE on input.
1954     UnicodeString berlin("Europe/Berlin");
1955     ec = U_MEMORY_ALLOCATION_ERROR;
1956     pResult = &TimeZone::getCanonicalID(berlin, canonicalID, ec);
1957     assertEquals("TimeZone::getCanonicalID(failure) should fail", U_MEMORY_ALLOCATION_ERROR, ec);
1958     assertTrue("TimeZone::getCanonicalID(failure) should return the dest string", pResult == &canonicalID);
1959 
1960     // Valid input should un-bogus the dest string.
1961     canonicalID.setToBogus();
1962     ec = U_ZERO_ERROR;
1963     pResult = &TimeZone::getCanonicalID(berlin, canonicalID, ec);
1964     assertSuccess("TimeZone::getCanonicalID(bogus dest) should succeed", ec, TRUE);
1965     assertTrue("TimeZone::getCanonicalID(bogus dest) should return the dest string", pResult == &canonicalID);
1966     assertFalse("TimeZone::getCanonicalID(bogus dest) should un-bogus the dest string", canonicalID.isBogus());
1967     assertEquals("TimeZone::getCanonicalID(bogus dest) unexpected result", canonicalID, berlin, TRUE);
1968 }
1969 
TestCanonicalID()1970 void TimeZoneTest::TestCanonicalID() {
1971 
1972     // Some canonical IDs in CLDR are defined as "Link"
1973     // in Olson tzdata.
1974     static const struct {
1975         const char *alias;
1976         const char *zone;
1977     } excluded1[] = {
1978         {"Africa/Khartoum", "Africa/Juba"},
1979         {"America/Shiprock", "America/Denver"}, // America/Shiprock is defined as a Link to America/Denver in tzdata
1980         {"America/Dominica", "America/Anguilla"},
1981         {"America/Grenada", "America/Anguilla"},
1982         {"America/Guadeloupe", "America/Anguilla"},
1983         {"America/Marigot", "America/Anguilla"},
1984         {"America/Montserrat", "America/Anguilla"},
1985         {"America/Port_of_Spain", "America/Anguilla"},
1986         {"America/St_Barthelemy", "America/Anguilla"},
1987         {"America/St_Kitts", "America/Anguilla"},
1988         {"America/St_Lucia", "America/Anguilla"},
1989         {"America/St_Thomas", "America/Anguilla"},
1990         {"America/St_Vincent", "America/Anguilla"},
1991         {"America/Tortola", "America/Anguilla"},
1992         {"America/Virgin", "America/Anguilla"},
1993         {"America/Curacao", "America/Aruba"},
1994         {"America/Kralendijk", "America/Aruba"},
1995         {"America/Lower_Princes", "America/Aruba"},
1996         {"Antarctica/South_Pole", "Antarctica/McMurdo"},
1997         {"Atlantic/Jan_Mayen", "Europe/Oslo"},
1998         {"Arctic/Longyearbyen", "Europe/Oslo"},
1999         {"Europe/Busingen", "Europe/Zurich"},
2000         {"Europe/Guernsey", "Europe/London"},
2001         {"Europe/Isle_of_Man", "Europe/London"},
2002         {"Europe/Jersey", "Europe/London"},
2003         {"Europe/Ljubljana", "Europe/Belgrade"},
2004         {"Europe/Podgorica", "Europe/Belgrade"},
2005         {"Europe/Sarajevo", "Europe/Belgrade"},
2006         {"Europe/Skopje", "Europe/Belgrade"},
2007         {"Europe/Zagreb", "Europe/Belgrade"},
2008         {"Europe/Bratislava", "Europe/Prague"},
2009         {"Europe/Mariehamn", "Europe/Helsinki"},
2010         {"Europe/San_Marino", "Europe/Rome"},
2011         {"Europe/Vatican", "Europe/Rome"},
2012         {"Europe/Vaduz", "Europe/Zurich"},
2013         {"Pacific/Auckland", "Antarctica/McMurdo"},
2014         {"Pacific/Johnston", "Pacific/Honolulu"},
2015         {0, 0}
2016     };
2017 
2018     // Following IDs are aliases of Etc/GMT in CLDR,
2019     // but Olson tzdata has 3 independent definitions
2020     // for Etc/GMT, Etc/UTC, Etc/UCT.
2021     // Until we merge them into one equivalent group
2022     // in zoneinfo.res, we exclude them in the test
2023     // below.
2024     static const char* excluded2[] = {
2025         "Etc/UCT", "UCT",
2026         "Etc/UTC", "UTC",
2027         "Etc/Universal", "Universal",
2028         "Etc/Zulu", "Zulu", 0
2029     };
2030 
2031     // Walk through equivalency groups
2032     UErrorCode ec = U_ZERO_ERROR;
2033     int32_t s_length, i, j, k;
2034     StringEnumeration* s = TimeZone::createEnumeration();
2035     if (s == NULL) {
2036         dataerrln("Unable to create TimeZone enumeration");
2037         return;
2038     }
2039     UnicodeString canonicalID, tmpCanonical;
2040     s_length = s->count(ec);
2041     for (i = 0; i < s_length;++i) {
2042         const UnicodeString *tzid = s->snext(ec);
2043         int32_t nEquiv = TimeZone::countEquivalentIDs(*tzid);
2044         if (nEquiv == 0) {
2045             continue;
2046         }
2047         UBool bFoundCanonical = FALSE;
2048         // Make sure getCanonicalID returns the exact same result
2049         // for all entries within a same equivalency group with some
2050         // exceptions listed in exluded1.
2051         // Also, one of them must be canonical id.
2052         for (j = 0; j < nEquiv; j++) {
2053             UnicodeString tmp = TimeZone::getEquivalentID(*tzid, j);
2054             TimeZone::getCanonicalID(tmp, tmpCanonical, ec);
2055             if (U_FAILURE(ec)) {
2056                 errln((UnicodeString)"FAIL: getCanonicalID(" + tmp + ") failed.");
2057                 ec = U_ZERO_ERROR;
2058                 continue;
2059             }
2060             // Some exceptional cases
2061             for (k = 0; excluded1[k].alias != 0; k++) {
2062                 if (tmpCanonical == excluded1[k].alias) {
2063                     tmpCanonical = excluded1[k].zone;
2064                     break;
2065                 }
2066             }
2067             if (j == 0) {
2068                 canonicalID = tmpCanonical;
2069             } else if (canonicalID != tmpCanonical) {
2070                 errln("FAIL: getCanonicalID(" + tmp + ") returned " + tmpCanonical + " expected:" + canonicalID);
2071             }
2072 
2073             if (canonicalID == tmp) {
2074                 bFoundCanonical = TRUE;
2075             }
2076         }
2077         // At least one ID in an equvalency group must match the
2078         // canonicalID
2079         if (bFoundCanonical == FALSE) {
2080             // test exclusion because of differences between Olson tzdata and CLDR
2081             UBool isExcluded = FALSE;
2082             for (k = 0; excluded2[k] != 0; k++) {
2083                 if (*tzid == UnicodeString(excluded2[k])) {
2084                     isExcluded = TRUE;
2085                     break;
2086                 }
2087             }
2088             if (isExcluded) {
2089                 continue;
2090             }
2091             errln((UnicodeString)"FAIL: No timezone ids match the canonical ID " + canonicalID);
2092         }
2093     }
2094     delete s;
2095 
2096     // Testing some special cases
2097     static const struct {
2098         const char *id;
2099         const char *expected;
2100         UBool isSystem;
2101     } data[] = {
2102         {"GMT-03", "GMT-03:00", FALSE},
2103         {"GMT+4", "GMT+04:00", FALSE},
2104         {"GMT-055", "GMT-00:55", FALSE},
2105         {"GMT+430", "GMT+04:30", FALSE},
2106         {"GMT-12:15", "GMT-12:15", FALSE},
2107         {"GMT-091015", "GMT-09:10:15", FALSE},
2108         {"GMT+1:90", 0, FALSE},
2109         {"America/Argentina/Buenos_Aires", "America/Buenos_Aires", TRUE},
2110         {"Etc/Unknown", "Etc/Unknown", FALSE},
2111         {"bogus", 0, FALSE},
2112         {"", 0, FALSE},
2113         {"America/Marigot", "America/Marigot", TRUE},     // Olson link, but CLDR canonical (#8953)
2114         {"Europe/Bratislava", "Europe/Bratislava", TRUE}, // Same as above
2115         {0, 0, FALSE}
2116     };
2117 
2118     UBool isSystemID;
2119     for (i = 0; data[i].id != 0; i++) {
2120         TimeZone::getCanonicalID(UnicodeString(data[i].id), canonicalID, isSystemID, ec);
2121         if (U_FAILURE(ec)) {
2122             if (ec != U_ILLEGAL_ARGUMENT_ERROR || data[i].expected != 0) {
2123                 errln((UnicodeString)"FAIL: getCanonicalID(\"" + data[i].id
2124                     + "\") returned status U_ILLEGAL_ARGUMENT_ERROR");
2125             }
2126             ec = U_ZERO_ERROR;
2127             continue;
2128         }
2129         if (canonicalID != data[i].expected) {
2130             dataerrln((UnicodeString)"FAIL: getCanonicalID(\"" + data[i].id
2131                 + "\") returned " + canonicalID + " - expected: " + data[i].expected);
2132         }
2133         if (isSystemID != data[i].isSystem) {
2134             dataerrln((UnicodeString)"FAIL: getCanonicalID(\"" + data[i].id
2135                 + "\") set " + isSystemID + " to isSystemID");
2136         }
2137     }
2138 }
2139 
2140 //
2141 //  Test Display Names, choosing zones and lcoales where there are multiple
2142 //                      meta-zones defined.
2143 //
2144 static struct   {
2145     const char            *zoneName;
2146     const char            *localeName;
2147     UBool                  summerTime;
2148     TimeZone::EDisplayType style;
2149     const char            *expectedDisplayName; }
2150  zoneDisplayTestData [] =  {
2151      //  zone id         locale   summer   format          expected display name
2152       {"Europe/London",     "en", FALSE, TimeZone::SHORT, "GMT"},
2153       {"Europe/London",     "en", FALSE, TimeZone::LONG,  "Greenwich Mean Time"},
2154       {"Europe/London",     "en", TRUE,  TimeZone::SHORT, "GMT+1" /*"BST"*/},
2155       {"Europe/London",     "en", TRUE,  TimeZone::LONG,  "British Summer Time"},
2156 
2157       {"America/Anchorage", "en", FALSE, TimeZone::SHORT, "AKST"},
2158       {"America/Anchorage", "en", FALSE, TimeZone::LONG,  "Alaska Standard Time"},
2159       {"America/Anchorage", "en", TRUE,  TimeZone::SHORT, "AKDT"},
2160       {"America/Anchorage", "en", TRUE,  TimeZone::LONG,  "Alaska Daylight Time"},
2161 
2162       // Southern Hemisphere, all data from meta:Australia_Western
2163       {"Australia/Perth",   "en", FALSE, TimeZone::SHORT, "GMT+8"/*"AWST"*/},
2164       {"Australia/Perth",   "en", FALSE, TimeZone::LONG,  "Australian Western Standard Time"},
2165       // Note: Perth does not observe DST currently. When display name is missing,
2166       // the localized GMT format with the current offset is used even daylight name was
2167       // requested. See #9350.
2168       {"Australia/Perth",   "en", TRUE,  TimeZone::SHORT, "GMT+8"/*"AWDT"*/},
2169       {"Australia/Perth",   "en", TRUE,  TimeZone::LONG,  "Australian Western Daylight Time"},
2170 
2171       {"America/Sao_Paulo",  "en", FALSE, TimeZone::SHORT, "GMT-3"/*"BRT"*/},
2172       {"America/Sao_Paulo",  "en", FALSE, TimeZone::LONG,  "Brasilia Standard Time"},
2173       {"America/Sao_Paulo",  "en", TRUE,  TimeZone::SHORT, "GMT-2"/*"BRST"*/},
2174       {"America/Sao_Paulo",  "en", TRUE,  TimeZone::LONG,  "Brasilia Summer Time"},
2175 
2176       // No Summer Time, but had it before 1983.
2177       {"Pacific/Honolulu",   "en", FALSE, TimeZone::SHORT, "HST"},
2178       {"Pacific/Honolulu",   "en", FALSE, TimeZone::LONG,  "Hawaii-Aleutian Standard Time"},
2179       {"Pacific/Honolulu",   "en", TRUE,  TimeZone::SHORT, "HDT"},
2180       {"Pacific/Honolulu",   "en", TRUE,  TimeZone::LONG,  "Hawaii-Aleutian Daylight Time"},
2181 
2182       // Northern, has Summer, not commonly used.
2183       {"Europe/Helsinki",    "en", FALSE, TimeZone::SHORT, "GMT+2"/*"EET"*/},
2184       {"Europe/Helsinki",    "en", FALSE, TimeZone::LONG,  "Eastern European Standard Time"},
2185       {"Europe/Helsinki",    "en", TRUE,  TimeZone::SHORT, "GMT+3"/*"EEST"*/},
2186       {"Europe/Helsinki",    "en", TRUE,  TimeZone::LONG,  "Eastern European Summer Time"},
2187 
2188       // Repeating the test data for DST.  The test data below trigger the problem reported
2189       // by Ticket#6644
2190       {"Europe/London",       "en", TRUE, TimeZone::SHORT, "GMT+1" /*"BST"*/},
2191       {"Europe/London",       "en", TRUE, TimeZone::LONG,  "British Summer Time"},
2192 
2193       {NULL, NULL, FALSE, TimeZone::SHORT, NULL}   // NULL values terminate list
2194     };
2195 
TestDisplayNamesMeta()2196 void TimeZoneTest::TestDisplayNamesMeta() {
2197     UErrorCode status = U_ZERO_ERROR;
2198     GregorianCalendar cal(*TimeZone::getGMT(), status);
2199     if (failure(status, "GregorianCalendar", TRUE)) return;
2200 
2201     UBool sawAnError = FALSE;
2202     for (int testNum   = 0; zoneDisplayTestData[testNum].zoneName != NULL; testNum++) {
2203         Locale locale  = Locale::createFromName(zoneDisplayTestData[testNum].localeName);
2204         TimeZone *zone = TimeZone::createTimeZone(zoneDisplayTestData[testNum].zoneName);
2205         UnicodeString displayName;
2206         zone->getDisplayName(zoneDisplayTestData[testNum].summerTime,
2207                              zoneDisplayTestData[testNum].style,
2208                              locale,
2209                              displayName);
2210         if (displayName != zoneDisplayTestData[testNum].expectedDisplayName) {
2211             char  name[100];
2212             UErrorCode status = U_ZERO_ERROR;
2213             displayName.extract(name, 100, NULL, status);
2214             if (isDevelopmentBuild) {
2215                 sawAnError = TRUE;
2216                 dataerrln("Incorrect time zone display name.  zone = \"%s\",\n"
2217                       "   locale = \"%s\",   style = %s,  Summertime = %d\n"
2218                       "   Expected \"%s\", "
2219                       "   Got \"%s\"\n   Error: %s", zoneDisplayTestData[testNum].zoneName,
2220                                          zoneDisplayTestData[testNum].localeName,
2221                                          zoneDisplayTestData[testNum].style==TimeZone::SHORT ?
2222                                             "SHORT" : "LONG",
2223                                          zoneDisplayTestData[testNum].summerTime,
2224                                          zoneDisplayTestData[testNum].expectedDisplayName,
2225                                          name,
2226                                          u_errorName(status));
2227             } else {
2228                 logln("Incorrect time zone display name.  zone = \"%s\",\n"
2229                       "   locale = \"%s\",   style = %s,  Summertime = %d\n"
2230                       "   Expected \"%s\", "
2231                       "   Got \"%s\"\n", zoneDisplayTestData[testNum].zoneName,
2232                                          zoneDisplayTestData[testNum].localeName,
2233                                          zoneDisplayTestData[testNum].style==TimeZone::SHORT ?
2234                                             "SHORT" : "LONG",
2235                                          zoneDisplayTestData[testNum].summerTime,
2236                                          zoneDisplayTestData[testNum].expectedDisplayName,
2237                                          name);
2238             }
2239         }
2240         delete zone;
2241     }
2242     if (sawAnError) {
2243         dataerrln("***Note: Errors could be the result of changes to zoneStrings locale data");
2244     }
2245 }
2246 
TestGetRegion()2247 void TimeZoneTest::TestGetRegion()
2248 {
2249     static const struct {
2250         const char *id;
2251         const char *region;
2252     } data[] = {
2253         {"America/Los_Angeles",             "US"},
2254         {"America/Indianapolis",            "US"},  // CLDR canonical, Olson backward
2255         {"America/Indiana/Indianapolis",    "US"},  // CLDR alias
2256         {"Mexico/General",                  "MX"},  // Link America/Mexico_City, Olson backward
2257         {"Etc/UTC",                         "001"},
2258         {"EST5EDT",                         "001"},
2259         {"PST",                             "US"},  // Link America/Los_Angeles
2260         {"Europe/Helsinki",                 "FI"},
2261         {"Europe/Mariehamn",                "AX"},  // Link Europe/Helsinki, but in zone.tab
2262         {"Asia/Riyadh",                     "SA"},
2263         // tz file solar87 was removed from tzdata2013i
2264         // {"Asia/Riyadh87",                   "001"}, // this should be "SA" actually, but not in zone.tab
2265         {"Etc/Unknown",                     0},  // CLDR canonical, but not a sysmte zone ID
2266         {"bogus",                           0},  // bogus
2267         {"GMT+08:00",                       0},  // a custom ID, not a system zone ID
2268         {0, 0}
2269     };
2270 
2271     int32_t i;
2272     char region[4];
2273     UErrorCode sts;
2274     for (i = 0; data[i].id; i++) {
2275         sts = U_ZERO_ERROR;
2276         TimeZone::getRegion(data[i].id, region, sizeof(region), sts);
2277         if (U_SUCCESS(sts)) {
2278             if (data[i].region == 0) {
2279                 errln((UnicodeString)"Fail: getRegion(\"" + data[i].id + "\") returns "
2280                     + region + " [expected: U_ILLEGAL_ARGUMENT_ERROR]");
2281             } else if (uprv_strcmp(region, data[i].region) != 0) {
2282                 errln((UnicodeString)"Fail: getRegion(\"" + data[i].id + "\") returns "
2283                     + region + " [expected: " + data[i].region + "]");
2284             }
2285         } else if (sts == U_ILLEGAL_ARGUMENT_ERROR) {
2286             if (data[i].region != 0) {
2287                 dataerrln((UnicodeString)"Fail: getRegion(\"" + data[i].id
2288                     + "\") returns error status U_ILLEGAL_ARGUMENT_ERROR [expected: "
2289                     + data[i].region + "]");
2290             }
2291         } else {
2292                 errln((UnicodeString)"Fail: getRegion(\"" + data[i].id
2293                     + "\") returns an unexpected error status");
2294         }
2295     }
2296 
2297     // Extra test cases for short buffer
2298     int32_t len;
2299     char region2[2];
2300     sts = U_ZERO_ERROR;
2301 
2302     len = TimeZone::getRegion("America/New_York", region2, sizeof(region2), sts);
2303     if (sts == U_ILLEGAL_ARGUMENT_ERROR) {
2304         dataerrln("Error calling TimeZone::getRegion");
2305     } else {
2306         if (sts != U_STRING_NOT_TERMINATED_WARNING) {
2307             errln("Expected U_STRING_NOT_TERMINATED_WARNING");
2308         }
2309         if (len != 2) { // length of "US"
2310             errln("Incorrect result length");
2311         }
2312         if (uprv_strncmp(region2, "US", 2) != 0) {
2313             errln("Incorrect result");
2314         }
2315     }
2316 
2317     char region1[1];
2318     sts = U_ZERO_ERROR;
2319 
2320     len = TimeZone::getRegion("America/Chicago", region1, sizeof(region1), sts);
2321     if (sts == U_ILLEGAL_ARGUMENT_ERROR) {
2322         dataerrln("Error calling TimeZone::getRegion");
2323     } else {
2324         if (sts != U_BUFFER_OVERFLOW_ERROR) {
2325             errln("Expected U_BUFFER_OVERFLOW_ERROR");
2326         }
2327         if (len != 2) { // length of "US"
2328             errln("Incorrect result length");
2329         }
2330     }
2331 }
2332 
TestGetUnknown()2333 void TimeZoneTest::TestGetUnknown() {
2334     const TimeZone &unknown = TimeZone::getUnknown();
2335     UnicodeString expectedID = UNICODE_STRING_SIMPLE("Etc/Unknown");
2336     UnicodeString id;
2337     assertEquals("getUnknown() wrong ID", expectedID, unknown.getID(id));
2338     assertTrue("getUnknown() wrong offset", 0 == unknown.getRawOffset());
2339     assertFalse("getUnknown() uses DST", unknown.useDaylightTime());
2340 }
2341 
TestGetWindowsID(void)2342 void TimeZoneTest::TestGetWindowsID(void) {
2343     static const struct {
2344         const char *id;
2345         const char *winid;
2346     } TESTDATA[] = {
2347         {"America/New_York",        "Eastern Standard Time"},
2348         {"America/Montreal",        "Eastern Standard Time"},
2349         {"America/Los_Angeles",     "Pacific Standard Time"},
2350         {"America/Vancouver",       "Pacific Standard Time"},
2351         {"Asia/Shanghai",           "China Standard Time"},
2352         {"Asia/Chongqing",          "China Standard Time"},
2353         {"America/Indianapolis",    "US Eastern Standard Time"},            // CLDR canonical name
2354         {"America/Indiana/Indianapolis",    "US Eastern Standard Time"},    // tzdb canonical name
2355         {"Asia/Khandyga",           "Yakutsk Standard Time"},
2356         {"Australia/Eucla",         ""}, // No Windows ID mapping
2357         {"Bogus",                   ""},
2358         {0,                         0},
2359     };
2360 
2361     for (int32_t i = 0; TESTDATA[i].id != 0; i++) {
2362         UErrorCode sts = U_ZERO_ERROR;
2363         UnicodeString windowsID;
2364 
2365         TimeZone::getWindowsID(UnicodeString(TESTDATA[i].id), windowsID, sts);
2366         assertSuccess(TESTDATA[i].id, sts);
2367         assertEquals(TESTDATA[i].id, UnicodeString(TESTDATA[i].winid), windowsID, TRUE);
2368     }
2369 }
2370 
TestGetIDForWindowsID(void)2371 void TimeZoneTest::TestGetIDForWindowsID(void) {
2372     static const struct {
2373         const char *winid;
2374         const char *region;
2375         const char *id;
2376     } TESTDATA[] = {
2377         {"Eastern Standard Time",   0,      "America/New_York"},
2378         {"Eastern Standard Time",   "US",   "America/New_York"},
2379         {"Eastern Standard Time",   "CA",   "America/Toronto"},
2380         {"Eastern Standard Time",   "CN",   "America/New_York"},
2381         {"China Standard Time",     0,      "Asia/Shanghai"},
2382         {"China Standard Time",     "CN",   "Asia/Shanghai"},
2383         {"China Standard Time",     "HK",   "Asia/Hong_Kong"},
2384         {"Mid-Atlantic Standard Time",  0,  ""}, // No tz database mapping
2385         {"Bogus",                   0,      ""},
2386         {0,                         0,      0},
2387     };
2388 
2389     for (int32_t i = 0; TESTDATA[i].winid != 0; i++) {
2390         UErrorCode sts = U_ZERO_ERROR;
2391         UnicodeString id;
2392 
2393         TimeZone::getIDForWindowsID(UnicodeString(TESTDATA[i].winid), TESTDATA[i].region,
2394                                     id, sts);
2395         assertSuccess(UnicodeString(TESTDATA[i].winid) + "/" + TESTDATA[i].region, sts);
2396         assertEquals(UnicodeString(TESTDATA[i].winid) + "/" + TESTDATA[i].region, TESTDATA[i].id, id, TRUE);
2397     }
2398 }
2399 
2400 #endif /* #if !UCONFIG_NO_FORMATTING */
2401