• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***********************************************************************
2  * COPYRIGHT:
3  * Copyright (c) 1997-2011, International Business Machines Corporation
4  * and others. All Rights Reserved.
5  ***********************************************************************/
6 
7 #include "unicode/utypes.h"
8 
9 #if !UCONFIG_NO_FORMATTING
10 
11 #include "unicode/timezone.h"
12 #include "unicode/simpletz.h"
13 #include "unicode/calendar.h"
14 #include "unicode/gregocal.h"
15 #include "unicode/resbund.h"
16 #include "unicode/strenum.h"
17 #include "tztest.h"
18 #include "cmemory.h"
19 #include "putilimp.h"
20 #include "cstring.h"
21 #include "olsontz.h"
22 
23 #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
24 
25 #define CASE(id,test) case id:                               \
26                           name = #test;                      \
27                           if (exec) {                        \
28                               logln(#test "---"); logln(""); \
29                               test();                        \
30                           }                                  \
31                           break
32 
33 // *****************************************************************************
34 // class TimeZoneTest
35 // *****************************************************************************
36 
37 // TODO: We should probably read following data at runtime, so we can update
38 // these values every release with necessary data changes.
39 const int32_t TimeZoneTest::REFERENCE_YEAR = 2009;
40 const char * TimeZoneTest::REFERENCE_DATA_VERSION = "2009d";
41 
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)42 void TimeZoneTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
43 {
44     if (exec) logln("TestSuite TestTimeZone");
45     switch (index) {
46         CASE(0, TestPRTOffset);
47         CASE(1, TestVariousAPI518);
48         CASE(2, TestGetAvailableIDs913);
49         CASE(3, TestGenericAPI);
50         CASE(4, TestRuleAPI);
51         CASE(5, TestShortZoneIDs);
52         CASE(6, TestCustomParse);
53         CASE(7, TestDisplayName);
54         CASE(8, TestDSTSavings);
55         CASE(9, TestAlternateRules);
56         CASE(10,TestCountries);
57         CASE(11,TestHistorical);
58         CASE(12,TestEquivalentIDs);
59         CASE(13, TestAliasedNames);
60         CASE(14, TestFractionalDST);
61         CASE(15, TestFebruary);
62         CASE(16, TestCanonicalID);
63         CASE(17, TestDisplayNamesMeta);
64         CASE(18, TestGetRegion);
65         CASE(19, TestGetAvailableIDsNew);
66         default: name = ""; break;
67     }
68 }
69 
70 const int32_t TimeZoneTest::millisPerHour = 3600000;
71 
72 // ---------------------------------------------------------------------------------
73 
74 /**
75  * Generic API testing for API coverage.
76  */
77 void
TestGenericAPI()78 TimeZoneTest::TestGenericAPI()
79 {
80     UnicodeString id("NewGMT");
81     int32_t offset = 12345;
82 
83     SimpleTimeZone *zone = new SimpleTimeZone(offset, id);
84     if (zone->useDaylightTime()) errln("FAIL: useDaylightTime should return FALSE");
85 
86     TimeZone* zoneclone = zone->clone();
87     if (!(*zoneclone == *zone)) errln("FAIL: clone or operator== failed");
88     zoneclone->setID("abc");
89     if (!(*zoneclone != *zone)) errln("FAIL: clone or operator!= failed");
90     delete zoneclone;
91 
92     zoneclone = zone->clone();
93     if (!(*zoneclone == *zone)) errln("FAIL: clone or operator== failed");
94     zoneclone->setRawOffset(45678);
95     if (!(*zoneclone != *zone)) errln("FAIL: clone or operator!= failed");
96 
97     SimpleTimeZone copy(*zone);
98     if (!(copy == *zone)) errln("FAIL: copy constructor or operator== failed");
99     copy = *(SimpleTimeZone*)zoneclone;
100     if (!(copy == *zoneclone)) errln("FAIL: assignment operator or operator== failed");
101 
102     TimeZone* saveDefault = TimeZone::createDefault();
103     logln((UnicodeString)"TimeZone::createDefault() => " + saveDefault->getID(id));
104     TimeZone* pstZone = TimeZone::createTimeZone("America/Los_Angeles");
105 
106     logln("call uprv_timezone() which uses the host");
107     logln("to get the difference in seconds between coordinated universal");
108     logln("time and local time. E.g., -28,800 for PST (GMT-8hrs)");
109 
110     int32_t tzoffset = uprv_timezone();
111     logln(UnicodeString("Value returned from uprv_timezone = ") + tzoffset);
112     // Invert sign because UNIX semantics are backwards
113     if (tzoffset < 0)
114         tzoffset = -tzoffset;
115     if ((*saveDefault == *pstZone) && (tzoffset != 28800)) {
116         errln("FAIL: t_timezone may be incorrect.  It is not 28800");
117     }
118 
119     if ((tzoffset % 900) != 0) {
120         /*
121          * Ticket#6364 and #7648
122          * A few time zones are using GMT offests not a multiple of 15 minutes.
123          * Therefore, we should not interpret such case as an error.
124          * We downgrade this from errln to infoln. When we see this message,
125          * we should examine if it is ignorable or not.
126          */
127         infoln("WARNING: t_timezone may be incorrect. It is not a multiple of 15min.", tzoffset);
128     }
129 
130     TimeZone::adoptDefault(zone);
131     TimeZone* defaultzone = TimeZone::createDefault();
132     if (defaultzone == zone ||
133         !(*defaultzone == *zone))
134         errln("FAIL: createDefault failed");
135     TimeZone::adoptDefault(saveDefault);
136     delete defaultzone;
137     delete zoneclone;
138     delete pstZone;
139 
140     UErrorCode status = U_ZERO_ERROR;
141     const char* tzver = TimeZone::getTZDataVersion(status);
142     if (U_FAILURE(status)) {
143         errcheckln(status, "FAIL: getTZDataVersion failed - %s", u_errorName(status));
144     } else if (uprv_strlen(tzver) != 5 /* 4 digits + 1 letter */) {
145         errln((UnicodeString)"FAIL: getTZDataVersion returned " + tzver);
146     } else {
147         logln((UnicodeString)"tzdata version: " + tzver);
148     }
149 }
150 
151 // ---------------------------------------------------------------------------------
152 
153 /**
154  * Test the setStartRule/setEndRule API calls.
155  */
156 void
TestRuleAPI()157 TimeZoneTest::TestRuleAPI()
158 {
159     UErrorCode status = U_ZERO_ERROR;
160 
161     UDate offset = 60*60*1000*1.75; // Pick a weird offset
162     SimpleTimeZone *zone = new SimpleTimeZone((int32_t)offset, "TestZone");
163     if (zone->useDaylightTime()) errln("FAIL: useDaylightTime should return FALSE");
164 
165     // Establish our expected transition times.  Do this with a non-DST
166     // calendar with the (above) declared local offset.
167     GregorianCalendar *gc = new GregorianCalendar(*zone, status);
168     if (failure(status, "new GregorianCalendar", TRUE)) return;
169     gc->clear();
170     gc->set(1990, UCAL_MARCH, 1);
171     UDate marchOneStd = gc->getTime(status); // Local Std time midnight
172     gc->clear();
173     gc->set(1990, UCAL_JULY, 1);
174     UDate julyOneStd = gc->getTime(status); // Local Std time midnight
175     if (failure(status, "GregorianCalendar::getTime")) return;
176 
177     // Starting and ending hours, WALL TIME
178     int32_t startHour = (int32_t)(2.25 * 3600000);
179     int32_t endHour   = (int32_t)(3.5  * 3600000);
180 
181     zone->setStartRule(UCAL_MARCH, 1, 0, startHour, status);
182     zone->setEndRule  (UCAL_JULY,  1, 0, endHour, status);
183 
184     delete gc;
185     gc = new GregorianCalendar(*zone, status);
186     if (failure(status, "new GregorianCalendar")) return;
187 
188     UDate marchOne = marchOneStd + startHour;
189     UDate julyOne = julyOneStd + endHour - 3600000; // Adjust from wall to Std time
190 
191     UDate expMarchOne = 636251400000.0;
192     if (marchOne != expMarchOne)
193     {
194         errln((UnicodeString)"FAIL: Expected start computed as " + marchOne +
195           " = " + dateToString(marchOne));
196         logln((UnicodeString)"      Should be                  " + expMarchOne +
197           " = " + dateToString(expMarchOne));
198     }
199 
200     UDate expJulyOne = 646793100000.0;
201     if (julyOne != expJulyOne)
202     {
203         errln((UnicodeString)"FAIL: Expected start computed as " + julyOne +
204           " = " + dateToString(julyOne));
205         logln((UnicodeString)"      Should be                  " + expJulyOne +
206           " = " + dateToString(expJulyOne));
207     }
208 
209     testUsingBinarySearch(*zone, date(90, UCAL_JANUARY, 1), date(90, UCAL_JUNE, 15), marchOne);
210     testUsingBinarySearch(*zone, date(90, UCAL_JUNE, 1), date(90, UCAL_DECEMBER, 31), julyOne);
211 
212     if (zone->inDaylightTime(marchOne - 1000, status) ||
213         !zone->inDaylightTime(marchOne, status))
214         errln("FAIL: Start rule broken");
215     if (!zone->inDaylightTime(julyOne - 1000, status) ||
216         zone->inDaylightTime(julyOne, status))
217         errln("FAIL: End rule broken");
218 
219     zone->setStartYear(1991);
220     if (zone->inDaylightTime(marchOne, status) ||
221         zone->inDaylightTime(julyOne - 1000, status))
222         errln("FAIL: Start year broken");
223 
224     failure(status, "TestRuleAPI");
225     delete gc;
226     delete zone;
227 }
228 
229 void
findTransition(const TimeZone & tz,UDate min,UDate max)230 TimeZoneTest::findTransition(const TimeZone& tz,
231                              UDate min, UDate max) {
232     UErrorCode ec = U_ZERO_ERROR;
233     UnicodeString id,s;
234     UBool startsInDST = tz.inDaylightTime(min, ec);
235     if (failure(ec, "TimeZone::inDaylightTime")) return;
236     if (tz.inDaylightTime(max, ec) == startsInDST) {
237         logln("Error: " + tz.getID(id) + ".inDaylightTime(" + dateToString(min) + ") = " + (startsInDST?"TRUE":"FALSE") +
238               ", inDaylightTime(" + dateToString(max) + ") = " + (startsInDST?"TRUE":"FALSE"));
239         return;
240     }
241     if (failure(ec, "TimeZone::inDaylightTime")) return;
242     while ((max - min) > INTERVAL) {
243         UDate mid = (min + max) / 2;
244         if (tz.inDaylightTime(mid, ec) == startsInDST) {
245             min = mid;
246         } else {
247             max = mid;
248         }
249         if (failure(ec, "TimeZone::inDaylightTime")) return;
250     }
251     min = 1000.0 * uprv_floor(min/1000.0);
252     max = 1000.0 * uprv_floor(max/1000.0);
253     logln(tz.getID(id) + " Before: " + min/1000 + " = " +
254           dateToString(min,s,tz));
255     logln(tz.getID(id) + " After:  " + max/1000 + " = " +
256           dateToString(max,s,tz));
257 }
258 
259 void
testUsingBinarySearch(const TimeZone & tz,UDate min,UDate max,UDate expectedBoundary)260 TimeZoneTest::testUsingBinarySearch(const TimeZone& tz,
261                                     UDate min, UDate max,
262                                     UDate expectedBoundary)
263 {
264     UErrorCode status = U_ZERO_ERROR;
265     UBool startsInDST = tz.inDaylightTime(min, status);
266     if (failure(status, "TimeZone::inDaylightTime")) return;
267     if (tz.inDaylightTime(max, status) == startsInDST) {
268         logln("Error: inDaylightTime(" + dateToString(max) + ") != " + ((!startsInDST)?"TRUE":"FALSE"));
269         return;
270     }
271     if (failure(status, "TimeZone::inDaylightTime")) return;
272     while ((max - min) > INTERVAL) {
273         UDate mid = (min + max) / 2;
274         if (tz.inDaylightTime(mid, status) == startsInDST) {
275             min = mid;
276         } else {
277             max = mid;
278         }
279         if (failure(status, "TimeZone::inDaylightTime")) return;
280     }
281     logln(UnicodeString("Binary Search Before: ") + uprv_floor(0.5 + min) + " = " + dateToString(min));
282     logln(UnicodeString("Binary Search After:  ") + uprv_floor(0.5 + max) + " = " + dateToString(max));
283     UDate mindelta = expectedBoundary - min;
284     UDate maxdelta = max - expectedBoundary;
285     if (mindelta >= 0 &&
286         mindelta <= INTERVAL &&
287         maxdelta >= 0 &&
288         maxdelta <= INTERVAL)
289         logln(UnicodeString("PASS: Expected bdry:  ") + expectedBoundary + " = " + dateToString(expectedBoundary));
290     else
291         errln(UnicodeString("FAIL: Expected bdry:  ") + expectedBoundary + " = " + dateToString(expectedBoundary));
292 }
293 
294 const UDate TimeZoneTest::INTERVAL = 100;
295 
296 // ---------------------------------------------------------------------------------
297 
298 // -------------------------------------
299 
300 /**
301  * Test the offset of the PRT timezone.
302  */
303 void
TestPRTOffset()304 TimeZoneTest::TestPRTOffset()
305 {
306     TimeZone* tz = TimeZone::createTimeZone("PRT");
307     if (tz == 0) {
308         errln("FAIL: TimeZone(PRT) is null");
309     }
310     else {
311       int32_t expectedHour = -4;
312       double expectedOffset = (((double)expectedHour) * millisPerHour);
313       double foundOffset = tz->getRawOffset();
314       int32_t foundHour = (int32_t)foundOffset / millisPerHour;
315       if (expectedOffset != foundOffset) {
316         dataerrln("FAIL: Offset for PRT should be %d, found %d", expectedHour, foundHour);
317       } else {
318         logln("PASS: Offset for PRT should be %d, found %d", expectedHour, foundHour);
319       }
320     }
321     delete tz;
322 }
323 
324 // -------------------------------------
325 
326 /**
327  * Regress a specific bug with a sequence of API calls.
328  */
329 void
TestVariousAPI518()330 TimeZoneTest::TestVariousAPI518()
331 {
332     UErrorCode status = U_ZERO_ERROR;
333     TimeZone* time_zone = TimeZone::createTimeZone("PST");
334     UDate d = date(97, UCAL_APRIL, 30);
335     UnicodeString str;
336     logln("The timezone is " + time_zone->getID(str));
337     if (!time_zone->inDaylightTime(d, status)) dataerrln("FAIL: inDaylightTime returned FALSE");
338     if (failure(status, "TimeZone::inDaylightTime", TRUE)) return;
339     if (!time_zone->useDaylightTime()) dataerrln("FAIL: useDaylightTime returned FALSE");
340     if (time_zone->getRawOffset() != - 8 * millisPerHour) dataerrln("FAIL: getRawOffset returned wrong value");
341     GregorianCalendar *gc = new GregorianCalendar(status);
342     if (U_FAILURE(status)) { errln("FAIL: Couldn't create GregorianCalendar"); return; }
343     gc->setTime(d, status);
344     if (U_FAILURE(status)) { errln("FAIL: GregorianCalendar::setTime failed"); return; }
345     if (time_zone->getOffset(gc->AD, gc->get(UCAL_YEAR, status), gc->get(UCAL_MONTH, status),
346         gc->get(UCAL_DATE, status), (uint8_t)gc->get(UCAL_DAY_OF_WEEK, status), 0, status) != - 7 * millisPerHour)
347         dataerrln("FAIL: getOffset returned wrong value");
348     if (U_FAILURE(status)) { errln("FAIL: GregorianCalendar::set failed"); return; }
349     delete gc;
350     delete time_zone;
351 }
352 
353 // -------------------------------------
354 
355 /**
356  * Test the call which retrieves the available IDs.
357  */
358 void
TestGetAvailableIDs913()359 TimeZoneTest::TestGetAvailableIDs913()
360 {
361     UErrorCode ec = U_ZERO_ERROR;
362     int32_t i;
363 
364 #ifdef U_USE_TIMEZONE_OBSOLETE_2_8
365     // Test legacy API -- remove these tests when the corresponding API goes away (duh)
366     int32_t numIDs = -1;
367     const UnicodeString** ids = TimeZone::createAvailableIDs(numIDs);
368     if (ids == 0 || numIDs < 1) {
369         errln("FAIL: createAvailableIDs()");
370     } else {
371         UnicodeString buf("TimeZone::createAvailableIDs() = { ");
372         for(i=0; i<numIDs; ++i) {
373             if (i) buf.append(", ");
374             buf.append(*ids[i]);
375         }
376         buf.append(" } ");
377         logln(buf + numIDs);
378         // we own the array; the caller owns the contained strings (yuck)
379         uprv_free(ids);
380     }
381 
382     numIDs = -1;
383     ids = TimeZone::createAvailableIDs(-8*U_MILLIS_PER_HOUR, numIDs);
384     if (ids == 0 || numIDs < 1) {
385         errln("FAIL: createAvailableIDs(-8:00)");
386     } else {
387         UnicodeString buf("TimeZone::createAvailableIDs(-8:00) = { ");
388         for(i=0; i<numIDs; ++i) {
389             if (i) buf.append(", ");
390             buf.append(*ids[i]);
391         }
392         buf.append(" } ");
393         logln(buf + numIDs);
394         // we own the array; the caller owns the contained strings (yuck)
395         uprv_free(ids);
396     }
397     numIDs = -1;
398     ids = TimeZone::createAvailableIDs("US", numIDs);
399     if (ids == 0 || numIDs < 1) {
400       errln("FAIL: createAvailableIDs(US) ids=%d, numIDs=%d", ids, numIDs);
401     } else {
402         UnicodeString buf("TimeZone::createAvailableIDs(US) = { ");
403         for(i=0; i<numIDs; ++i) {
404             if (i) buf.append(", ");
405             buf.append(*ids[i]);
406         }
407         buf.append(" } ");
408         logln(buf + numIDs);
409         // we own the array; the caller owns the contained strings (yuck)
410         uprv_free(ids);
411     }
412 #endif
413 
414     UnicodeString str;
415     UnicodeString *buf = new UnicodeString("TimeZone::createEnumeration() = { ");
416     int32_t s_length;
417     StringEnumeration* s = TimeZone::createEnumeration();
418     if (s == NULL) {
419         dataerrln("Unable to create TimeZone enumeration");
420         return;
421     }
422     s_length = s->count(ec);
423     for (i = 0; i < s_length;++i) {
424         if (i > 0) *buf += ", ";
425         if ((i & 1) == 0) {
426             *buf += *s->snext(ec);
427         } else {
428             *buf += UnicodeString(s->next(NULL, ec), "");
429         }
430 
431         if((i % 5) == 4) {
432             // replace s with a clone of itself
433             StringEnumeration *s2 = s->clone();
434             if(s2 == NULL || s_length != s2->count(ec)) {
435                 errln("TimezoneEnumeration.clone() failed");
436             } else {
437                 delete s;
438                 s = s2;
439             }
440         }
441     }
442     *buf += " };";
443     logln(*buf);
444 
445     /* Confirm that the following zones can be retrieved: The first
446      * zone, the last zone, and one in-between.  This tests the binary
447      * search through the system zone data.
448      */
449     s->reset(ec);
450     int32_t middle = s_length/2;
451     for (i=0; i<s_length; ++i) {
452         const UnicodeString* id = s->snext(ec);
453         if (i==0 || i==middle || i==(s_length-1)) {
454         TimeZone *z = TimeZone::createTimeZone(*id);
455         if (z == 0) {
456             errln(UnicodeString("FAIL: createTimeZone(") +
457                   *id + ") -> 0");
458         } else if (z->getID(str) != *id) {
459             errln(UnicodeString("FAIL: createTimeZone(") +
460                   *id + ") -> zone " + str);
461         } else {
462             logln(UnicodeString("OK: createTimeZone(") +
463                   *id + ") succeeded");
464         }
465         delete z;
466         }
467     }
468     delete s;
469 
470     buf->truncate(0);
471     *buf += "TimeZone::createEnumeration(GMT+01:00) = { ";
472 
473     s = TimeZone::createEnumeration(1 * U_MILLIS_PER_HOUR);
474     s_length = s->count(ec);
475     for (i = 0; i < s_length;++i) {
476         if (i > 0) *buf += ", ";
477         *buf += *s->snext(ec);
478     }
479     delete s;
480     *buf += " };";
481     logln(*buf);
482 
483 
484     buf->truncate(0);
485     *buf += "TimeZone::createEnumeration(US) = { ";
486 
487     s = TimeZone::createEnumeration("US");
488     s_length = s->count(ec);
489     for (i = 0; i < s_length;++i) {
490         if (i > 0) *buf += ", ";
491         *buf += *s->snext(ec);
492     }
493     *buf += " };";
494     logln(*buf);
495 
496     TimeZone *tz = TimeZone::createTimeZone("PST");
497     if (tz != 0) logln("getTimeZone(PST) = " + tz->getID(str));
498     else errln("FAIL: getTimeZone(PST) = null");
499     delete tz;
500     tz = TimeZone::createTimeZone("America/Los_Angeles");
501     if (tz != 0) logln("getTimeZone(America/Los_Angeles) = " + tz->getID(str));
502     else errln("FAIL: getTimeZone(PST) = null");
503     delete tz;
504 
505     // @bug 4096694
506     tz = TimeZone::createTimeZone("NON_EXISTENT");
507     UnicodeString temp;
508     if (tz == 0)
509         errln("FAIL: getTimeZone(NON_EXISTENT) = null");
510     else if (tz->getID(temp) != UCAL_UNKNOWN_ZONE_ID)
511         errln("FAIL: getTimeZone(NON_EXISTENT) = " + temp);
512     delete tz;
513 
514     delete buf;
515     delete s;
516 }
517 
518 void
TestGetAvailableIDsNew()519 TimeZoneTest::TestGetAvailableIDsNew()
520 {
521     UErrorCode ec = U_ZERO_ERROR;
522     StringEnumeration *any, *canonical, *canonicalLoc;
523     StringEnumeration *any_US, *canonical_US, *canonicalLoc_US;
524     StringEnumeration *any_W5, *any_CA_W5;
525     StringEnumeration *any_US_E14;
526     int32_t rawOffset;
527     const UnicodeString *id1, *id2;
528     UnicodeString canonicalID;
529     UBool isSystemID;
530     char region[4];
531     int32_t zoneCount;
532 
533     any = canonical = canonicalLoc = any_US = canonical_US = canonicalLoc_US = any_W5 = any_CA_W5 = any_US_E14 = NULL;
534 
535     any = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, NULL, NULL, ec);
536     if (U_FAILURE(ec)) {
537         dataerrln("Failed to create enumration for ANY");
538         goto cleanup;
539     }
540 
541     canonical = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, ec);
542     if (U_FAILURE(ec)) {
543         errln("Failed to create enumration for CANONICAL");
544         goto cleanup;
545     }
546 
547     canonicalLoc = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, NULL, NULL, ec);
548     if (U_FAILURE(ec)) {
549         errln("Failed to create enumration for CANONICALLOC");
550         goto cleanup;
551     }
552 
553     any_US = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, "US", NULL, ec);
554     if (U_FAILURE(ec)) {
555         errln("Failed to create enumration for ANY_US");
556         goto cleanup;
557     }
558 
559     canonical_US = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, "US", NULL, ec);
560     if (U_FAILURE(ec)) {
561         errln("Failed to create enumration for CANONICAL_US");
562         goto cleanup;
563     }
564 
565     canonicalLoc_US = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, "US", NULL, ec);
566     if (U_FAILURE(ec)) {
567         errln("Failed to create enumration for CANONICALLOC_US");
568         goto cleanup;
569     }
570 
571     rawOffset = (-5)*60*60*1000;
572     any_W5 = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, NULL, &rawOffset, ec);
573     if (U_FAILURE(ec)) {
574         errln("Failed to create enumration for ANY_W5");
575         goto cleanup;
576     }
577 
578     any_CA_W5 = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, "CA", &rawOffset, ec);
579     if (U_FAILURE(ec)) {
580         errln("Failed to create enumration for ANY_CA_W5");
581         goto cleanup;
582     }
583 
584     rawOffset = 14*60*60*1000;
585     any_US_E14 = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, "US", &rawOffset, ec);
586     if (U_FAILURE(ec)) {
587         errln("Failed to create enumration for ANY_US_E14");
588         goto cleanup;
589     }
590 
591     checkContainsAll(any, "ANY", canonical, "CANONICAL");
592     checkContainsAll(canonical, "CANONICAL", canonicalLoc, "CANONICALLOC");
593 
594     checkContainsAll(any, "ANY", any_US, "ANY_US");
595     checkContainsAll(canonical, "CANONICAL", canonical_US, "CANONICAL_US");
596     checkContainsAll(canonicalLoc, "CANONICALLOC", canonicalLoc_US, "CANONICALLOC_US");
597 
598     checkContainsAll(any_US, "ANY_US", canonical_US, "CANONICAL_US");
599     checkContainsAll(canonical_US, "CANONICAL_US", canonicalLoc_US, "CANONICALLOC_US");
600 
601     checkContainsAll(any, "ANY", any_W5, "ANY_W5");
602     checkContainsAll(any_W5, "ANY_W5", any_CA_W5, "ANY_CA_W5");
603 
604     // And ID in any set, but not in canonical set must not be a canonical ID
605     any->reset(ec);
606     while ((id1 = any->snext(ec)) != NULL) {
607         UBool found = FALSE;
608         canonical->reset(ec);
609         while ((id2 = canonical->snext(ec)) != NULL) {
610             if (*id1 == *id2) {
611                 found = TRUE;
612                 break;
613             }
614         }
615         if (U_FAILURE(ec)) {
616             break;
617         }
618         if (!found) {
619             TimeZone::getCanonicalID(*id1, canonicalID, isSystemID, ec);
620             if (U_FAILURE(ec)) {
621                 break;
622             }
623             if (*id1 == canonicalID) {
624                 errln((UnicodeString)"FAIL: canonicalID [" + *id1 + "] is not in CANONICAL");
625             }
626             if (!isSystemID) {
627                 errln((UnicodeString)"FAIL: ANY contains non-system ID: " + *id1);
628             }
629         }
630     }
631     if (U_FAILURE(ec)) {
632         errln("Error checking IDs in ANY, but not in CANONICAL");
633         ec = U_ZERO_ERROR;
634     }
635 
636     // canonical set must contains only canonical IDs
637     canonical->reset(ec);
638     while ((id1 = canonical->snext(ec)) != NULL) {
639         TimeZone::getCanonicalID(*id1, canonicalID, isSystemID, ec);
640         if (U_FAILURE(ec)) {
641             break;
642         }
643         if (*id1 != canonicalID) {
644             errln((UnicodeString)"FAIL: CANONICAL contains non-canonical ID: " + *id1);
645         }
646         if (!isSystemID) {
647             errln((UnicodeString)"FAILE: CANONICAL contains non-system ID: " + *id1);
648         }
649     }
650     if (U_FAILURE(ec)) {
651         errln("Error checking IDs in CANONICAL");
652         ec = U_ZERO_ERROR;
653     }
654 
655     // canonicalLoc set must contain only canonical location IDs
656     canonicalLoc->reset(ec);
657     while ((id1 = canonicalLoc->snext(ec)) != NULL) {
658         TimeZone::getRegion(*id1, region, sizeof(region), ec);
659         if (U_FAILURE(ec)) {
660             break;
661         }
662         if (uprv_strcmp(region, "001") == 0) {
663             errln((UnicodeString)"FAIL: CANONICALLOC contains non location zone: " + *id1);
664         }
665     }
666     if (U_FAILURE(ec)) {
667         errln("Error checking IDs in CANONICALLOC");
668         ec = U_ZERO_ERROR;
669     }
670 
671     // any_US must contain only US zones
672     any_US->reset(ec);
673     while ((id1 = any_US->snext(ec)) != NULL) {
674         TimeZone::getRegion(*id1, region, sizeof(region), ec);
675         if (U_FAILURE(ec)) {
676             break;
677         }
678         if (uprv_strcmp(region, "US") != 0) {
679             errln((UnicodeString)"FAIL: ANY_US contains non-US zone ID: " + *id1);
680         }
681     }
682     if (U_FAILURE(ec)) {
683         errln("Error checking IDs in ANY_US");
684         ec = U_ZERO_ERROR;
685     }
686 
687     // any_W5 must contain only GMT-05:00 zones
688     any_W5->reset(ec);
689     while ((id1 = any_W5->snext(ec)) != NULL) {
690         TimeZone *tz = TimeZone::createTimeZone(*id1);
691         if (tz->getRawOffset() != (-5)*60*60*1000) {
692             errln((UnicodeString)"FAIL: ANY_W5 contains a zone whose offset is not -05:00: " + *id1);
693         }
694         delete tz;
695     }
696     if (U_FAILURE(ec)) {
697         errln("Error checking IDs in ANY_W5");
698         ec = U_ZERO_ERROR;
699     }
700 
701     // No US zone swith GMT+14:00
702     zoneCount = any_US_E14->count(ec);
703     if (U_FAILURE(ec)) {
704         errln("Error checking IDs in ANY_US_E14");
705         ec = U_ZERO_ERROR;
706     } else if (zoneCount != 0) {
707         errln("FAIL: ANY_US_E14 must be empty");
708     }
709 
710 cleanup:
711     delete any;
712     delete canonical;
713     delete canonicalLoc;
714     delete any_US;
715     delete canonical_US;
716     delete canonicalLoc_US;
717     delete any_W5;
718     delete any_CA_W5;
719     delete any_US_E14;
720 }
721 
722 void
checkContainsAll(StringEnumeration * s1,const char * name1,StringEnumeration * s2,const char * name2)723 TimeZoneTest::checkContainsAll(StringEnumeration *s1, const char *name1,
724         StringEnumeration *s2, const char *name2)
725 {
726     UErrorCode ec = U_ZERO_ERROR;
727     const UnicodeString *id1, *id2;
728 
729     s2->reset(ec);
730 
731     while ((id2 = s2->snext(ec)) != NULL) {
732         UBool found = FALSE;
733         s1->reset(ec);
734         while ((id1 = s1->snext(ec)) != NULL) {
735             if (*id1 == *id2) {
736                 found = TRUE;
737                 break;
738             }
739         }
740         if (!found) {
741             errln((UnicodeString)"FAIL: " + name1 + "does not contain "
742                 + *id2 + " in " + name2);
743         }
744     }
745 
746     if (U_FAILURE(ec)) {
747         errln((UnicodeString)"Error checkContainsAll for " + name1 + " - " + name2);
748     }
749 }
750 
751 /**
752  * NOTE: As of ICU 2.8, this test confirms that the "tz.alias"
753  * file, used to build ICU alias zones, is working.  It also
754  * looks at some genuine Olson compatibility IDs. [aliu]
755  *
756  * This test is problematic. It should really just confirm that
757  * the list of compatibility zone IDs exist and are somewhat
758  * meaningful (that is, they aren't all aliases of GMT). It goes a
759  * bit further -- it hard-codes expectations about zone behavior,
760  * when in fact zones are redefined quite frequently. ICU's build
761  * process means that it is easy to update ICU to contain the
762  * latest Olson zone data, but if a zone tested here changes, then
763  * this test will fail.  I have updated the test for 1999j data,
764  * but further updates will probably be required. Note that some
765  * of the concerts listed below no longer apply -- in particular,
766  * we do NOT overwrite real UNIX zones with 3-letter IDs. There
767  * are two points of overlap as of 1999j: MET and EET. These are
768  * both real UNIX zones, so we just use the official
769  * definition. This test has been updated to reflect this.
770  * 12/3/99 aliu
771  *
772  * Added tests for additional zones and aliases from the icuzones file.
773  * Markus Scherer 2006-nov-06
774  *
775  * [srl - from java - 7/5/1998]
776  * @bug 4130885
777  * Certain short zone IDs, used since 1.1.x, are incorrect.
778  *
779  * The worst of these is:
780  *
781  * "CAT" (Central African Time) should be GMT+2:00, but instead returns a
782  * zone at GMT-1:00. The zone at GMT-1:00 should be called EGT, CVT, EGST,
783  * or AZOST, depending on which zone is meant, but in no case is it CAT.
784  *
785  * Other wrong zone IDs:
786  *
787  * ECT (European Central Time) GMT+1:00: ECT is Ecuador Time,
788  * GMT-5:00. European Central time is abbreviated CEST.
789  *
790  * SST (Solomon Island Time) GMT+11:00. SST is actually Samoa Standard Time,
791  * GMT-11:00. Solomon Island time is SBT.
792  *
793  * NST (New Zealand Time) GMT+12:00. NST is the abbreviation for
794  * Newfoundland Standard Time, GMT-3:30. New Zealanders use NZST.
795  *
796  * AST (Alaska Standard Time) GMT-9:00. [This has already been noted in
797  * another bug.] It should be "AKST". AST is Atlantic Standard Time,
798  * GMT-4:00.
799  *
800  * PNT (Phoenix Time) GMT-7:00. PNT usually means Pitcairn Time,
801  * GMT-8:30. There is no standard abbreviation for Phoenix time, as distinct
802  * from MST with daylight savings.
803  *
804  * In addition to these problems, a number of zones are FAKE. That is, they
805  * don't match what people use in the real world.
806  *
807  * FAKE zones:
808  *
809  * EET (should be EEST)
810  * ART (should be EEST)
811  * MET (should be IRST)
812  * NET (should be AMST)
813  * PLT (should be PKT)
814  * BST (should be BDT)
815  * VST (should be ICT)
816  * CTT (should be CST) +
817  * ACT (should be CST) +
818  * AET (should be EST) +
819  * MIT (should be WST) +
820  * IET (should be EST) +
821  * PRT (should be AST) +
822  * CNT (should be NST)
823  * AGT (should be ARST)
824  * BET (should be EST) +
825  *
826  * + A zone with the correct name already exists and means something
827  * else. E.g., EST usually indicates the US Eastern zone, so it cannot be
828  * used for Brazil (BET).
829  */
TestShortZoneIDs()830 void TimeZoneTest::TestShortZoneIDs()
831 {
832     UErrorCode status = U_ZERO_ERROR;
833 
834     // This test case is tzdata version sensitive.
835     UBool isNonReferenceTzdataVersion = FALSE;
836     const char *tzdataVer = TimeZone::getTZDataVersion(status);
837     if (failure(status, "getTZDataVersion")) return;
838     if (uprv_strcmp(tzdataVer, TimeZoneTest::REFERENCE_DATA_VERSION) != 0) {
839         // Note: We want to display a warning message here if
840         // REFERENCE_DATA_VERSION is out of date - so we
841         // do not forget to update the value before GA.
842         isNonReferenceTzdataVersion = TRUE;
843         logln(UnicodeString("Warning: Active tzdata version (") + tzdataVer +
844             ") does not match the reference tzdata version ("
845             + REFERENCE_DATA_VERSION + ") for this test case data.");
846     }
847 
848     // Note: useDaylightTime returns true if DST is observed
849     // in the time zone in the current calendar year.  The test
850     // data is valid for the date after the reference year below.
851     // If system clock is before the year, some test cases may
852     // fail.
853     GregorianCalendar cal(*TimeZone::getGMT(), status);
854     if (failure(status, "GregorianCalendar")) return;
855     cal.set(TimeZoneTest::REFERENCE_YEAR, UCAL_JANUARY, 2); // day 2 in GMT
856 
857     UBool isDateBeforeReferenceYear = ucal_getNow() < cal.getTime(status);
858     if (failure(status, "Calendar::getTime")) return;
859     if (isDateBeforeReferenceYear) {
860         logln("Warning: Past time is set to the system clock.  Some test cases may not return expected results.");
861     }
862 
863     int32_t i;
864     // Create a small struct to hold the array
865     struct
866     {
867         const char *id;
868         int32_t    offset;
869         UBool      daylight;
870     }
871     kReferenceList [] =
872     {
873         {"MIT", -660, FALSE},
874         {"HST", -600, FALSE},
875         {"AST", -540, TRUE},
876         {"PST", -480, TRUE},
877         {"PNT", -420, FALSE},
878         {"MST", -420, FALSE}, // updated Aug 2003 aliu
879         {"CST", -360, TRUE},
880         {"IET", -300, TRUE},  // updated Jan 2006 srl
881         {"EST", -300, FALSE}, // updated Aug 2003 aliu
882         {"PRT", -240, FALSE},
883         {"CNT", -210, TRUE},
884         {"AGT", -180, TRUE}, // updated by tzdata2007k
885         {"BET", -180, TRUE},
886         {"GMT", 0, FALSE},
887         {"UTC", 0, FALSE}, // ** srl: seems broken in C++
888         {"ECT", 60, TRUE},
889         {"MET", 60, TRUE}, // updated 12/3/99 aliu
890         {"ART", 120, TRUE},
891         {"EET", 120, TRUE},
892         {"CAT", 120, FALSE}, // Africa/Harare
893         {"EAT", 180, FALSE},
894         {"NET", 240, TRUE}, // updated 12/3/99 aliu
895         {"PLT", 300, FALSE}, // updated by 2008c - no DST after 2008
896         {"IST", 330, FALSE},
897         {"BST", 360, FALSE},
898         {"VST", 420, FALSE},
899         {"CTT", 480, FALSE}, // updated Aug 2003 aliu
900         {"JST", 540, FALSE},
901         {"ACT", 570, FALSE}, // updated Aug 2003 aliu
902         {"AET", 600, TRUE},
903         {"SST", 660, FALSE},
904         {"NST", 720, TRUE}, // Pacific/Auckland
905 
906         // From icuzones:
907         {"Etc/Unknown", 0, FALSE},
908 
909         {"SystemV/AST4ADT", -240, TRUE},
910         {"SystemV/EST5EDT", -300, TRUE},
911         {"SystemV/CST6CDT", -360, TRUE},
912         {"SystemV/MST7MDT", -420, TRUE},
913         {"SystemV/PST8PDT", -480, TRUE},
914         {"SystemV/YST9YDT", -540, TRUE},
915         {"SystemV/AST4", -240, FALSE},
916         {"SystemV/EST5", -300, FALSE},
917         {"SystemV/CST6", -360, FALSE},
918         {"SystemV/MST7", -420, FALSE},
919         {"SystemV/PST8", -480, FALSE},
920         {"SystemV/YST9", -540, FALSE},
921         {"SystemV/HST10", -600, FALSE},
922 
923         {"",0,FALSE}
924     };
925 
926     for(i=0;kReferenceList[i].id[0];i++) {
927         UnicodeString itsID(kReferenceList[i].id);
928         UBool ok = TRUE;
929         // Check existence.
930         TimeZone *tz = TimeZone::createTimeZone(itsID);
931         if (!tz || (kReferenceList[i].offset != 0 && *tz == *TimeZone::getGMT())) {
932             errln("FAIL: Time Zone " + itsID + " does not exist!");
933             continue;
934         }
935 
936         // Check daylight usage.
937         UBool usesDaylight = tz->useDaylightTime();
938         if (usesDaylight != kReferenceList[i].daylight) {
939             if (isNonReferenceTzdataVersion || isDateBeforeReferenceYear) {
940                 logln("Warning: Time Zone " + itsID + " use daylight is " +
941                       (usesDaylight?"TRUE":"FALSE") +
942                       " but it should be " +
943                       ((kReferenceList[i].daylight)?"TRUE":"FALSE"));
944             } else {
945                 errln("FAIL: Time Zone " + itsID + " use daylight is " +
946                       (usesDaylight?"TRUE":"FALSE") +
947                       " but it should be " +
948                       ((kReferenceList[i].daylight)?"TRUE":"FALSE"));
949             }
950             ok = FALSE;
951         }
952 
953         // Check offset
954         int32_t offsetInMinutes = tz->getRawOffset()/60000;
955         if (offsetInMinutes != kReferenceList[i].offset) {
956             if (isNonReferenceTzdataVersion || isDateBeforeReferenceYear) {
957                 logln("FAIL: Time Zone " + itsID + " raw offset is " +
958                       offsetInMinutes +
959                       " but it should be " + kReferenceList[i].offset);
960             } else {
961                 errln("FAIL: Time Zone " + itsID + " raw offset is " +
962                       offsetInMinutes +
963                       " but it should be " + kReferenceList[i].offset);
964             }
965             ok = FALSE;
966         }
967 
968         if (ok) {
969             logln("OK: " + itsID +
970                   " useDaylightTime() & getRawOffset() as expected");
971         }
972         delete tz;
973     }
974 
975 
976     // OK now test compat
977     logln("Testing for compatibility zones");
978 
979     const char* compatibilityMap[] = {
980         // This list is copied from tz.alias.  If tz.alias
981         // changes, this list must be updated.  Current as of Mar 2007
982         "ACT", "Australia/Darwin",
983         "AET", "Australia/Sydney",
984         "AGT", "America/Buenos_Aires",
985         "ART", "Africa/Cairo",
986         "AST", "America/Anchorage",
987         "BET", "America/Sao_Paulo",
988         "BST", "Asia/Dhaka", // # spelling changed in 2000h; was Asia/Dacca
989         "CAT", "Africa/Harare",
990         "CNT", "America/St_Johns",
991         "CST", "America/Chicago",
992         "CTT", "Asia/Shanghai",
993         "EAT", "Africa/Addis_Ababa",
994         "ECT", "Europe/Paris",
995         // EET Europe/Istanbul # EET is a standard UNIX zone
996         // "EST", "America/New_York", # Defined as -05:00
997         // "HST", "Pacific/Honolulu", # Defined as -10:00
998         "IET", "America/Indianapolis",
999         "IST", "Asia/Calcutta",
1000         "JST", "Asia/Tokyo",
1001         // MET Asia/Tehran # MET is a standard UNIX zone
1002         "MIT", "Pacific/Apia",
1003         // "MST", "America/Denver", # Defined as -07:00
1004         "NET", "Asia/Yerevan",
1005         "NST", "Pacific/Auckland",
1006         "PLT", "Asia/Karachi",
1007         "PNT", "America/Phoenix",
1008         "PRT", "America/Puerto_Rico",
1009         "PST", "America/Los_Angeles",
1010         "SST", "Pacific/Guadalcanal",
1011         "UTC", "Etc/GMT",
1012         "VST", "Asia/Saigon",
1013          "","",""
1014     };
1015 
1016     for (i=0;*compatibilityMap[i];i+=2) {
1017         UnicodeString itsID;
1018 
1019         const char *zone1 = compatibilityMap[i];
1020         const char *zone2 = compatibilityMap[i+1];
1021 
1022         TimeZone *tz1 = TimeZone::createTimeZone(zone1);
1023         TimeZone *tz2 = TimeZone::createTimeZone(zone2);
1024 
1025         if (!tz1) {
1026             errln(UnicodeString("FAIL: Could not find short ID zone ") + zone1);
1027         }
1028         if (!tz2) {
1029             errln(UnicodeString("FAIL: Could not find long ID zone ") + zone2);
1030         }
1031 
1032         if (tz1 && tz2) {
1033             // make NAME same so comparison will only look at the rest
1034             tz2->setID(tz1->getID(itsID));
1035 
1036             if (*tz1 != *tz2) {
1037                 errln("FAIL: " + UnicodeString(zone1) +
1038                       " != " + UnicodeString(zone2));
1039             } else {
1040                 logln("OK: " + UnicodeString(zone1) +
1041                       " == " + UnicodeString(zone2));
1042             }
1043         }
1044 
1045         delete tz1;
1046         delete tz2;
1047     }
1048 }
1049 
1050 
1051 /**
1052  * Utility function for TestCustomParse
1053  */
formatOffset(int32_t offset,UnicodeString & rv)1054 UnicodeString& TimeZoneTest::formatOffset(int32_t offset, UnicodeString &rv) {
1055     rv.remove();
1056     UChar sign = 0x002B;
1057     if (offset < 0) {
1058         sign = 0x002D;
1059         offset = -offset;
1060     }
1061 
1062     int32_t s = offset % 60;
1063     offset /= 60;
1064     int32_t m = offset % 60;
1065     int32_t h = offset / 60;
1066 
1067     rv += (UChar)(sign);
1068     if (h >= 10) {
1069         rv += (UChar)(0x0030 + (h/10));
1070     } else {
1071         rv += (UChar)0x0030;
1072     }
1073     rv += (UChar)(0x0030 + (h%10));
1074 
1075     rv += (UChar)0x003A; /* ':' */
1076     if (m >= 10) {
1077         rv += (UChar)(0x0030 + (m/10));
1078     } else {
1079         rv += (UChar)0x0030;
1080     }
1081     rv += (UChar)(0x0030 + (m%10));
1082 
1083     if (s) {
1084         rv += (UChar)0x003A; /* ':' */
1085         if (s >= 10) {
1086             rv += (UChar)(0x0030 + (s/10));
1087         } else {
1088             rv += (UChar)0x0030;
1089         }
1090         rv += (UChar)(0x0030 + (s%10));
1091     }
1092     return rv;
1093 }
1094 
1095 /**
1096  * Utility function for TestCustomParse, generating time zone ID
1097  * string for the give offset.
1098  */
formatTZID(int32_t offset,UnicodeString & rv)1099 UnicodeString& TimeZoneTest::formatTZID(int32_t offset, UnicodeString &rv) {
1100     rv.remove();
1101     UChar sign = 0x002B;
1102     if (offset < 0) {
1103         sign = 0x002D;
1104         offset = -offset;
1105     }
1106 
1107     int32_t s = offset % 60;
1108     offset /= 60;
1109     int32_t m = offset % 60;
1110     int32_t h = offset / 60;
1111 
1112     rv += "GMT";
1113     rv += (UChar)(sign);
1114     if (h >= 10) {
1115         rv += (UChar)(0x0030 + (h/10));
1116     } else {
1117         rv += (UChar)0x0030;
1118     }
1119     rv += (UChar)(0x0030 + (h%10));
1120     rv += (UChar)0x003A;
1121     if (m >= 10) {
1122         rv += (UChar)(0x0030 + (m/10));
1123     } else {
1124         rv += (UChar)0x0030;
1125     }
1126     rv += (UChar)(0x0030 + (m%10));
1127 
1128     if (s) {
1129         rv += (UChar)0x003A;
1130         if (s >= 10) {
1131             rv += (UChar)(0x0030 + (s/10));
1132         } else {
1133             rv += (UChar)0x0030;
1134         }
1135         rv += (UChar)(0x0030 + (s%10));
1136     }
1137     return rv;
1138 }
1139 
1140 /**
1141  * As part of the VM fix (see CCC approved RFE 4028006, bug
1142  * 4044013), TimeZone.getTimeZone() has been modified to recognize
1143  * generic IDs of the form GMT[+-]hh:mm, GMT[+-]hhmm, and
1144  * GMT[+-]hh.  Test this behavior here.
1145  *
1146  * @bug 4044013
1147  */
TestCustomParse()1148 void TimeZoneTest::TestCustomParse()
1149 {
1150     int32_t i;
1151     const int32_t kUnparseable = 604800; // the number of seconds in a week. More than any offset should be.
1152 
1153     struct
1154     {
1155         const char *customId;
1156         int32_t expectedOffset;
1157     }
1158     kData[] =
1159     {
1160         // ID        Expected offset in seconds
1161         {"GMT",       kUnparseable},   //Isn't custom. [returns normal GMT]
1162         {"GMT-YOUR.AD.HERE", kUnparseable},
1163         {"GMT0",      kUnparseable},
1164         {"GMT+0",     (0)},
1165         {"GMT+1",     (1*60*60)},
1166         {"GMT-0030",  (-30*60)},
1167         {"GMT+15:99", kUnparseable},
1168         {"GMT+",      kUnparseable},
1169         {"GMT-",      kUnparseable},
1170         {"GMT+0:",    kUnparseable},
1171         {"GMT-:",     kUnparseable},
1172         {"GMT-YOUR.AD.HERE",    kUnparseable},
1173         {"GMT+0010",  (10*60)}, // Interpret this as 00:10
1174         {"GMT-10",    (-10*60*60)},
1175         {"GMT+30",    kUnparseable},
1176         {"GMT-3:30",  (-(3*60+30)*60)},
1177         {"GMT-230",   (-(2*60+30)*60)},
1178         {"GMT+05:13:05",    ((5*60+13)*60+5)},
1179         {"GMT-71023",       (-((7*60+10)*60+23))},
1180         {"GMT+01:23:45:67", kUnparseable},
1181         {"GMT+01:234",      kUnparseable},
1182         {"GMT-2:31:123",    kUnparseable},
1183         {"GMT+3:75",        kUnparseable},
1184         {"GMT-01010101",    kUnparseable},
1185         {0,           0}
1186     };
1187 
1188     for (i=0; kData[i].customId != 0; i++) {
1189         UnicodeString id(kData[i].customId);
1190         int32_t exp = kData[i].expectedOffset;
1191         TimeZone *zone = TimeZone::createTimeZone(id);
1192         UnicodeString   itsID, temp;
1193 
1194         if (dynamic_cast<OlsonTimeZone *>(zone) != NULL) {
1195             logln(id + " -> Olson time zone");
1196         } else {
1197             zone->getID(itsID);
1198             int32_t ioffset = zone->getRawOffset()/1000;
1199             UnicodeString offset, expectedID;
1200             formatOffset(ioffset, offset);
1201             formatTZID(ioffset, expectedID);
1202             logln(id + " -> " + itsID + " " + offset);
1203             if (exp == kUnparseable && itsID != UCAL_UNKNOWN_ZONE_ID) {
1204                 errln("Expected parse failure for " + id +
1205                       ", got offset of " + offset +
1206                       ", id " + itsID);
1207             }
1208             // JDK 1.3 creates custom zones with the ID "Custom"
1209             // JDK 1.4 creates custom zones with IDs of the form "GMT+02:00"
1210             // ICU creates custom zones with IDs of the form "GMT+02:00"
1211             else if (exp != kUnparseable && (ioffset != exp || itsID != expectedID)) {
1212                 dataerrln("Expected offset of " + formatOffset(exp, temp) +
1213                       ", id " + expectedID +
1214                       ", for " + id +
1215                       ", got offset of " + offset +
1216                       ", id " + itsID);
1217             }
1218         }
1219         delete zone;
1220     }
1221 }
1222 
1223 void
TestAliasedNames()1224 TimeZoneTest::TestAliasedNames()
1225 {
1226     struct {
1227         const char *from;
1228         const char *to;
1229     } kData[] = {
1230         /* Generated by org.unicode.cldr.tool.CountItems */
1231 
1232         /* zoneID, canonical zoneID */
1233         {"Africa/Timbuktu", "Africa/Bamako"},
1234         {"America/Argentina/Buenos_Aires", "America/Buenos_Aires"},
1235         {"America/Argentina/Catamarca", "America/Catamarca"},
1236         {"America/Argentina/ComodRivadavia", "America/Catamarca"},
1237         {"America/Argentina/Cordoba", "America/Cordoba"},
1238         {"America/Argentina/Jujuy", "America/Jujuy"},
1239         {"America/Argentina/Mendoza", "America/Mendoza"},
1240         {"America/Atka", "America/Adak"},
1241         {"America/Ensenada", "America/Tijuana"},
1242         {"America/Fort_Wayne", "America/Indiana/Indianapolis"},
1243         {"America/Indianapolis", "America/Indiana/Indianapolis"},
1244         {"America/Knox_IN", "America/Indiana/Knox"},
1245         {"America/Louisville", "America/Kentucky/Louisville"},
1246         {"America/Porto_Acre", "America/Rio_Branco"},
1247         {"America/Rosario", "America/Cordoba"},
1248         {"America/Virgin", "America/St_Thomas"},
1249         {"Asia/Ashkhabad", "Asia/Ashgabat"},
1250         {"Asia/Chungking", "Asia/Chongqing"},
1251         {"Asia/Dacca", "Asia/Dhaka"},
1252         {"Asia/Istanbul", "Europe/Istanbul"},
1253         {"Asia/Macao", "Asia/Macau"},
1254         {"Asia/Tel_Aviv", "Asia/Jerusalem"},
1255         {"Asia/Thimbu", "Asia/Thimphu"},
1256         {"Asia/Ujung_Pandang", "Asia/Makassar"},
1257         {"Asia/Ulan_Bator", "Asia/Ulaanbaatar"},
1258         {"Australia/ACT", "Australia/Sydney"},
1259         {"Australia/Canberra", "Australia/Sydney"},
1260         {"Australia/LHI", "Australia/Lord_Howe"},
1261         {"Australia/NSW", "Australia/Sydney"},
1262         {"Australia/North", "Australia/Darwin"},
1263         {"Australia/Queensland", "Australia/Brisbane"},
1264         {"Australia/South", "Australia/Adelaide"},
1265         {"Australia/Tasmania", "Australia/Hobart"},
1266         {"Australia/Victoria", "Australia/Melbourne"},
1267         {"Australia/West", "Australia/Perth"},
1268         {"Australia/Yancowinna", "Australia/Broken_Hill"},
1269         {"Brazil/Acre", "America/Rio_Branco"},
1270         {"Brazil/DeNoronha", "America/Noronha"},
1271         {"Brazil/East", "America/Sao_Paulo"},
1272         {"Brazil/West", "America/Manaus"},
1273         {"Canada/Atlantic", "America/Halifax"},
1274         {"Canada/Central", "America/Winnipeg"},
1275         {"Canada/East-Saskatchewan", "America/Regina"},
1276         {"Canada/Eastern", "America/Toronto"},
1277         {"Canada/Mountain", "America/Edmonton"},
1278         {"Canada/Newfoundland", "America/St_Johns"},
1279         {"Canada/Pacific", "America/Vancouver"},
1280         {"Canada/Saskatchewan", "America/Regina"},
1281         {"Canada/Yukon", "America/Whitehorse"},
1282         {"Chile/Continental", "America/Santiago"},
1283         {"Chile/EasterIsland", "Pacific/Easter"},
1284         {"Cuba", "America/Havana"},
1285         {"Egypt", "Africa/Cairo"},
1286         {"Eire", "Europe/Dublin"},
1287         {"Etc/GMT+0", "Etc/GMT"},
1288         {"Etc/GMT-0", "Etc/GMT"},
1289         {"Etc/GMT0", "Etc/GMT"},
1290         {"Etc/Greenwich", "Etc/GMT"},
1291         {"Etc/UCT", "Etc/GMT"},
1292         {"Etc/UTC", "Etc/GMT"},
1293         {"Etc/Universal", "Etc/GMT"},
1294         {"Etc/Zulu", "Etc/GMT"},
1295         {"Europe/Belfast", "Europe/London"},
1296         {"Europe/Nicosia", "Asia/Nicosia"},
1297         {"Europe/Tiraspol", "Europe/Chisinau"},
1298         {"GB", "Europe/London"},
1299         {"GB-Eire", "Europe/London"},
1300         {"GMT", "Etc/GMT"},
1301         {"GMT+0", "Etc/GMT"},
1302         {"GMT-0", "Etc/GMT"},
1303         {"GMT0", "Etc/GMT"},
1304         {"Greenwich", "Etc/GMT"},
1305         {"Hongkong", "Asia/Hong_Kong"},
1306         {"Iceland", "Atlantic/Reykjavik"},
1307         {"Iran", "Asia/Tehran"},
1308         {"Israel", "Asia/Jerusalem"},
1309         {"Jamaica", "America/Jamaica"},
1310         {"Japan", "Asia/Tokyo"},
1311         {"Kwajalein", "Pacific/Kwajalein"},
1312         {"Libya", "Africa/Tripoli"},
1313         {"Mexico/BajaNorte", "America/Tijuana"},
1314         {"Mexico/BajaSur", "America/Mazatlan"},
1315         {"Mexico/General", "America/Mexico_City"},
1316         {"NZ", "Pacific/Auckland"},
1317         {"NZ-CHAT", "Pacific/Chatham"},
1318         {"Navajo", "America/Shiprock"},
1319         {"PRC", "Asia/Shanghai"},
1320         {"Pacific/Samoa", "Pacific/Pago_Pago"},
1321         {"Pacific/Yap", "Pacific/Truk"},
1322         {"Poland", "Europe/Warsaw"},
1323         {"Portugal", "Europe/Lisbon"},
1324         {"ROC", "Asia/Taipei"},
1325         {"ROK", "Asia/Seoul"},
1326         {"Singapore", "Asia/Singapore"},
1327         {"Turkey", "Europe/Istanbul"},
1328         {"UCT", "Etc/GMT"},
1329         {"US/Alaska", "America/Anchorage"},
1330         {"US/Aleutian", "America/Adak"},
1331         {"US/Arizona", "America/Phoenix"},
1332         {"US/Central", "America/Chicago"},
1333         {"US/East-Indiana", "America/Indiana/Indianapolis"},
1334         {"US/Eastern", "America/New_York"},
1335         {"US/Hawaii", "Pacific/Honolulu"},
1336         {"US/Indiana-Starke", "America/Indiana/Knox"},
1337         {"US/Michigan", "America/Detroit"},
1338         {"US/Mountain", "America/Denver"},
1339         {"US/Pacific", "America/Los_Angeles"},
1340         {"US/Pacific-New", "America/Los_Angeles"},
1341         {"US/Samoa", "Pacific/Pago_Pago"},
1342         {"UTC", "Etc/GMT"},
1343         {"Universal", "Etc/GMT"},
1344         {"W-SU", "Europe/Moscow"},
1345         {"Zulu", "Etc/GMT"},
1346         /* Total: 113 */
1347 
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 < (int32_t)(sizeof(kData)/sizeof(kData[0])); 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 < (int32_t)(sizeof(styles)/sizeof(styles[0])); j++) {
1367                     for(k = 0; k < (int32_t)(sizeof(useDst)/sizeof(useDst[0])); 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,  "United States Time (Los Angeles)"},
1447         {TRUE,  TimeZone::GENERIC_LOCATION,  "United States Time (Los Angeles)"},
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 < LENGTHOF(timezones); ++t) {
1943         tz = timezones[t];
1944         for (i = 0; i < 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 }
TestCanonicalID()1974 void TimeZoneTest::TestCanonicalID() {
1975 
1976     // Some canonical IDs in CLDR are defined as "Link"
1977     // in Olson tzdata.
1978     static const struct {
1979         const char *alias;
1980         const char *zone;
1981     } excluded1[] = {
1982         {"America/Shiprock", "America/Denver"}, // America/Shiprock is defined as a Link to America/Denver in tzdata
1983         {"America/Marigot", "America/Guadeloupe"},
1984         {"America/St_Barthelemy", "America/Guadeloupe"},
1985         {"Antarctica/South_Pole", "Antarctica/McMurdo"},
1986         {"Atlantic/Jan_Mayen", "Europe/Oslo"},
1987         {"Arctic/Longyearbyen", "Europe/Oslo"},
1988         {"Europe/Guernsey", "Europe/London"},
1989         {"Europe/Isle_of_Man", "Europe/London"},
1990         {"Europe/Jersey", "Europe/London"},
1991         {"Europe/Ljubljana", "Europe/Belgrade"},
1992         {"Europe/Podgorica", "Europe/Belgrade"},
1993         {"Europe/Sarajevo", "Europe/Belgrade"},
1994         {"Europe/Skopje", "Europe/Belgrade"},
1995         {"Europe/Zagreb", "Europe/Belgrade"},
1996         {"Europe/Bratislava", "Europe/Prague"},
1997         {"Europe/Mariehamn", "Europe/Helsinki"},
1998         {"Europe/San_Marino", "Europe/Rome"},
1999         {"Europe/Vatican", "Europe/Rome"},
2000         {0, 0}
2001     };
2002 
2003     // Following IDs are aliases of Etc/GMT in CLDR,
2004     // but Olson tzdata has 3 independent definitions
2005     // for Etc/GMT, Etc/UTC, Etc/UCT.
2006     // Until we merge them into one equivalent group
2007     // in zoneinfo.res, we exclude them in the test
2008     // below.
2009     static const char* excluded2[] = {
2010         "Etc/UCT", "UCT",
2011         "Etc/UTC", "UTC",
2012         "Etc/Universal", "Universal",
2013         "Etc/Zulu", "Zulu", 0
2014     };
2015 
2016     // Walk through equivalency groups
2017     UErrorCode ec = U_ZERO_ERROR;
2018     int32_t s_length, i, j, k;
2019     StringEnumeration* s = TimeZone::createEnumeration();
2020     if (s == NULL) {
2021         dataerrln("Unable to create TimeZone enumeration");
2022         return;
2023     }
2024     UnicodeString canonicalID, tmpCanonical;
2025     s_length = s->count(ec);
2026     for (i = 0; i < s_length;++i) {
2027         const UnicodeString *tzid = s->snext(ec);
2028         int32_t nEquiv = TimeZone::countEquivalentIDs(*tzid);
2029         if (nEquiv == 0) {
2030             continue;
2031         }
2032         UBool bFoundCanonical = FALSE;
2033         // Make sure getCanonicalID returns the exact same result
2034         // for all entries within a same equivalency group with some
2035         // exceptions listed in exluded1.
2036         // Also, one of them must be canonical id.
2037         for (j = 0; j < nEquiv; j++) {
2038             UnicodeString tmp = TimeZone::getEquivalentID(*tzid, j);
2039             TimeZone::getCanonicalID(tmp, tmpCanonical, ec);
2040             if (U_FAILURE(ec)) {
2041                 errln((UnicodeString)"FAIL: getCanonicalID(" + tmp + ") failed.");
2042                 ec = U_ZERO_ERROR;
2043                 continue;
2044             }
2045             // Some exceptional cases
2046             for (k = 0; excluded1[k].alias != 0; k++) {
2047                 if (tmpCanonical == excluded1[k].alias) {
2048                     tmpCanonical = excluded1[k].zone;
2049                     break;
2050                 }
2051             }
2052             if (j == 0) {
2053                 canonicalID = tmpCanonical;
2054             } else if (canonicalID != tmpCanonical) {
2055                 errln("FAIL: getCanonicalID(" + tmp + ") returned " + tmpCanonical + " expected:" + canonicalID);
2056             }
2057 
2058             if (canonicalID == tmp) {
2059                 bFoundCanonical = TRUE;
2060             }
2061         }
2062         // At least one ID in an equvalency group must match the
2063         // canonicalID
2064         if (bFoundCanonical == FALSE) {
2065             // test exclusion because of differences between Olson tzdata and CLDR
2066             UBool isExcluded = FALSE;
2067             for (k = 0; excluded2[k] != 0; k++) {
2068                 if (*tzid == UnicodeString(excluded2[k])) {
2069                     isExcluded = TRUE;
2070                     break;
2071                 }
2072             }
2073             if (isExcluded) {
2074                 continue;
2075             }
2076             errln((UnicodeString)"FAIL: No timezone ids match the canonical ID " + canonicalID);
2077         }
2078     }
2079     delete s;
2080 
2081     // Testing some special cases
2082     static const struct {
2083         const char *id;
2084         const char *expected;
2085         UBool isSystem;
2086     } data[] = {
2087         {"GMT-03", "GMT-03:00", FALSE},
2088         {"GMT+4", "GMT+04:00", FALSE},
2089         {"GMT-055", "GMT-00:55", FALSE},
2090         {"GMT+430", "GMT+04:30", FALSE},
2091         {"GMT-12:15", "GMT-12:15", FALSE},
2092         {"GMT-091015", "GMT-09:10:15", FALSE},
2093         {"GMT+1:90", 0, FALSE},
2094         {"America/Argentina/Buenos_Aires", "America/Buenos_Aires", TRUE},
2095         {"Etc/Unknown", "Etc/Unknown", FALSE},
2096         {"bogus", 0, FALSE},
2097         {"", 0, FALSE},
2098         {0, 0, FALSE}
2099     };
2100 
2101     UBool isSystemID;
2102     for (i = 0; data[i].id != 0; i++) {
2103         TimeZone::getCanonicalID(UnicodeString(data[i].id), canonicalID, isSystemID, ec);
2104         if (U_FAILURE(ec)) {
2105             if (ec != U_ILLEGAL_ARGUMENT_ERROR || data[i].expected != 0) {
2106                 errln((UnicodeString)"FAIL: getCanonicalID(\"" + data[i].id
2107                     + "\") returned status U_ILLEGAL_ARGUMENT_ERROR");
2108             }
2109             ec = U_ZERO_ERROR;
2110             continue;
2111         }
2112         if (canonicalID != data[i].expected) {
2113             dataerrln((UnicodeString)"FAIL: getCanonicalID(\"" + data[i].id
2114                 + "\") returned " + canonicalID + " - expected: " + data[i].expected);
2115         }
2116         if (isSystemID != data[i].isSystem) {
2117             dataerrln((UnicodeString)"FAIL: getCanonicalID(\"" + data[i].id
2118                 + "\") set " + isSystemID + " to isSystemID");
2119         }
2120     }
2121 }
2122 
2123 //
2124 //  Test Display Names, choosing zones and lcoales where there are multiple
2125 //                      meta-zones defined.
2126 //
2127 static struct   {
2128     const char            *zoneName;
2129     const char            *localeName;
2130     UBool                  summerTime;
2131     TimeZone::EDisplayType style;
2132     const char            *expectedDisplayName; }
2133  zoneDisplayTestData [] =  {
2134      //  zone id         locale   summer   format          expected display name
2135       {"Europe/London",     "en", FALSE, TimeZone::SHORT, "GMT"},
2136       {"Europe/London",     "en", FALSE, TimeZone::LONG,  "Greenwich Mean Time"},
2137       {"Europe/London",     "en", TRUE,  TimeZone::SHORT, "GMT+01:00" /*"BST"*/},
2138       {"Europe/London",     "en", TRUE,  TimeZone::LONG,  "British Summer Time"},
2139 
2140       {"America/Anchorage", "en", FALSE, TimeZone::SHORT, "AKST"},
2141       {"America/Anchorage", "en", FALSE, TimeZone::LONG,  "Alaska Standard Time"},
2142       {"America/Anchorage", "en", TRUE,  TimeZone::SHORT, "AKDT"},
2143       {"America/Anchorage", "en", TRUE,  TimeZone::LONG,  "Alaska Daylight Time"},
2144 
2145       // Southern Hemisphere, all data from meta:Australia_Western
2146       {"Australia/Perth",   "en", FALSE, TimeZone::SHORT, "GMT+08:00"/*"AWST"*/},
2147       {"Australia/Perth",   "en", FALSE, TimeZone::LONG,  "Australian Western Standard Time"},
2148       {"Australia/Perth",   "en", TRUE,  TimeZone::SHORT, "GMT+09:00"/*"AWDT"*/},
2149       {"Australia/Perth",   "en", TRUE,  TimeZone::LONG,  "Australian Western Daylight Time"},
2150 
2151       {"America/Sao_Paulo",  "en", FALSE, TimeZone::SHORT, "GMT-03:00"/*"BRT"*/},
2152       {"America/Sao_Paulo",  "en", FALSE, TimeZone::LONG,  "Brasilia Time"},
2153       {"America/Sao_Paulo",  "en", TRUE,  TimeZone::SHORT, "GMT-02:00"/*"BRST"*/},
2154       {"America/Sao_Paulo",  "en", TRUE,  TimeZone::LONG,  "Brasilia Summer Time"},
2155 
2156       // No Summer Time, but had it before 1983.
2157       {"Pacific/Honolulu",   "en", FALSE, TimeZone::SHORT, "HST"},
2158       {"Pacific/Honolulu",   "en", FALSE, TimeZone::LONG,  "Hawaii-Aleutian Standard Time"},
2159       {"Pacific/Honolulu",   "en", TRUE,  TimeZone::SHORT, "HST"},
2160       {"Pacific/Honolulu",   "en", TRUE,  TimeZone::LONG,  "Hawaii-Aleutian Standard Time"},
2161 
2162       // Northern, has Summer, not commonly used.
2163       {"Europe/Helsinki",    "en", FALSE, TimeZone::SHORT, "GMT+02:00"/*"EET"*/},
2164       {"Europe/Helsinki",    "en", FALSE, TimeZone::LONG,  "Eastern European Time"},
2165       {"Europe/Helsinki",    "en", TRUE,  TimeZone::SHORT, "GMT+03:00"/*"EEST"*/},
2166       {"Europe/Helsinki",    "en", true,  TimeZone::LONG,  "Eastern European Summer Time"},
2167       {NULL, NULL, FALSE, TimeZone::SHORT, NULL}   // NULL values terminate list
2168     };
2169 
TestDisplayNamesMeta()2170 void TimeZoneTest::TestDisplayNamesMeta() {
2171     UErrorCode status = U_ZERO_ERROR;
2172     GregorianCalendar cal(*TimeZone::getGMT(), status);
2173     if (failure(status, "GregorianCalendar", TRUE)) return;
2174 
2175     UBool isReferenceYear = TRUE;
2176     if (cal.get(UCAL_YEAR, status) != TimeZoneTest::REFERENCE_YEAR) {
2177         isReferenceYear = FALSE;
2178     }
2179 
2180     UBool sawAnError = FALSE;
2181     for (int testNum   = 0; zoneDisplayTestData[testNum].zoneName != NULL; testNum++) {
2182         Locale locale  = Locale::createFromName(zoneDisplayTestData[testNum].localeName);
2183         TimeZone *zone = TimeZone::createTimeZone(zoneDisplayTestData[testNum].zoneName);
2184         UnicodeString displayName;
2185         zone->getDisplayName(zoneDisplayTestData[testNum].summerTime,
2186                              zoneDisplayTestData[testNum].style,
2187                              locale,
2188                              displayName);
2189         if (displayName != zoneDisplayTestData[testNum].expectedDisplayName) {
2190             char  name[100];
2191             UErrorCode status = U_ZERO_ERROR;
2192             displayName.extract(name, 100, NULL, status);
2193             if (isReferenceYear) {
2194                 sawAnError = TRUE;
2195                 dataerrln("Incorrect time zone display name.  zone = \"%s\",\n"
2196                       "   locale = \"%s\",   style = %s,  Summertime = %d\n"
2197                       "   Expected \"%s\", "
2198                       "   Got \"%s\"\n   Error: %s", zoneDisplayTestData[testNum].zoneName,
2199                                          zoneDisplayTestData[testNum].localeName,
2200                                          zoneDisplayTestData[testNum].style==TimeZone::SHORT ?
2201                                             "SHORT" : "LONG",
2202                                          zoneDisplayTestData[testNum].summerTime,
2203                                          zoneDisplayTestData[testNum].expectedDisplayName,
2204                                          name,
2205                                          u_errorName(status));
2206             } else {
2207                 logln("Incorrect time zone display name.  zone = \"%s\",\n"
2208                       "   locale = \"%s\",   style = %s,  Summertime = %d\n"
2209                       "   Expected \"%s\", "
2210                       "   Got \"%s\"\n", zoneDisplayTestData[testNum].zoneName,
2211                                          zoneDisplayTestData[testNum].localeName,
2212                                          zoneDisplayTestData[testNum].style==TimeZone::SHORT ?
2213                                             "SHORT" : "LONG",
2214                                          zoneDisplayTestData[testNum].summerTime,
2215                                          zoneDisplayTestData[testNum].expectedDisplayName,
2216                                          name);
2217             }
2218         }
2219         delete zone;
2220     }
2221     if (sawAnError) {
2222         dataerrln("***Note: Errors could be the result of changes to zoneStrings locale data");
2223     }
2224 }
2225 
TestGetRegion()2226 void TimeZoneTest::TestGetRegion()
2227 {
2228     static const struct {
2229         const char *id;
2230         const char *region;
2231     } data[] = {
2232         {"America/Los_Angeles",             "US"},
2233         {"America/Indianapolis",            "US"},  // CLDR canonical, Olson backward
2234         {"America/Indiana/Indianapolis",    "US"},  // CLDR alias
2235         {"Mexico/General",                  "MX"},  // Link America/Mexico_City, Olson backward
2236         {"Etc/UTC",                         "001"},
2237         {"EST5EDT",                         "001"},
2238         {"PST",                             "US"},  // Link America/Los_Angeles
2239         {"Europe/Helsinki",                 "FI"},
2240         {"Europe/Mariehamn",                "AX"},  // Link Europe/Helsinki, but in zone.tab
2241         {"Asia/Riyadh",                     "SA"},
2242         {"Asia/Riyadh87",                   "001"}, // this should be "SA" actually, but not in zone.tab
2243         {"Etc/Unknown",                     0},  // CLDR canonical, but not a sysmte zone ID
2244         {"bogus",                           0},  // bogus
2245         {"GMT+08:00",                       0},  // a custom ID, not a system zone ID
2246         {0, 0}
2247     };
2248 
2249     int32_t i;
2250     char region[4];
2251     UErrorCode sts;
2252     for (i = 0; data[i].id; i++) {
2253         sts = U_ZERO_ERROR;
2254         TimeZone::getRegion(data[i].id, region, sizeof(region), sts);
2255         if (U_SUCCESS(sts)) {
2256             if (data[i].region == 0) {
2257                 errln((UnicodeString)"Fail: getRegion(\"" + data[i].id + "\") returns "
2258                     + region + " [expected: U_ILLEGAL_ARGUMENT_ERROR]");
2259             } else if (uprv_strcmp(region, data[i].region) != 0) {
2260                 errln((UnicodeString)"Fail: getRegion(\"" + data[i].id + "\") returns "
2261                     + region + " [expected: " + data[i].region + "]");
2262             }
2263         } else if (sts == U_ILLEGAL_ARGUMENT_ERROR) {
2264             if (data[i].region != 0) {
2265                 dataerrln((UnicodeString)"Fail: getRegion(\"" + data[i].id
2266                     + "\") returns error status U_ILLEGAL_ARGUMENT_ERROR [expected: "
2267                     + data[i].region + "]");
2268             }
2269         } else {
2270                 errln((UnicodeString)"Fail: getRegion(\"" + data[i].id
2271                     + "\") returns an unexpected error status");
2272         }
2273     }
2274 
2275     // Extra test cases for short buffer
2276     int32_t len;
2277     char region2[2];
2278     sts = U_ZERO_ERROR;
2279 
2280     len = TimeZone::getRegion("America/New_York", region2, sizeof(region2), sts);
2281     if (sts == U_ILLEGAL_ARGUMENT_ERROR) {
2282         dataerrln("Error calling TimeZone::getRegion");
2283     } else {
2284         if (sts != U_STRING_NOT_TERMINATED_WARNING) {
2285             errln("Expected U_STRING_NOT_TERMINATED_WARNING");
2286         }
2287         if (len != 2) { // length of "US"
2288             errln("Incorrect result length");
2289         }
2290         if (uprv_strncmp(region2, "US", 2) != 0) {
2291             errln("Incorrect result");
2292         }
2293     }
2294 
2295     char region1[1];
2296     sts = U_ZERO_ERROR;
2297 
2298     len = TimeZone::getRegion("America/Chicago", region1, sizeof(region1), sts);
2299     if (sts == U_ILLEGAL_ARGUMENT_ERROR) {
2300         dataerrln("Error calling TimeZone::getRegion");
2301     } else {
2302         if (sts != U_BUFFER_OVERFLOW_ERROR) {
2303             errln("Expected U_BUFFER_OVERFLOW_ERROR");
2304         }
2305         if (len != 2) { // length of "US"
2306             errln("Incorrect result length");
2307         }
2308     }
2309 }
2310 #endif /* #if !UCONFIG_NO_FORMATTING */
2311