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