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