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