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