]> git.saurik.com Git - apple/icu.git/blame - icuSources/test/intltest/tztest.cpp
ICU-400.42.tar.gz
[apple/icu.git] / icuSources / test / intltest / tztest.cpp
CommitLineData
374ca955 1/***********************************************************************
b75a7d8f 2 * COPYRIGHT:
46f4442e 3 * Copyright (c) 1997-2009, International Business Machines Corporation
374ca955
A
4 * and others. All Rights Reserved.
5 ***********************************************************************/
b75a7d8f
A
6
7#include "unicode/utypes.h"
8
9#if !UCONFIG_NO_FORMATTING
10
11#include "unicode/timezone.h"
12#include "unicode/simpletz.h"
13#include "unicode/calendar.h"
14#include "unicode/gregocal.h"
15#include "unicode/resbund.h"
16#include "unicode/strenum.h"
17#include "tztest.h"
18#include "cmemory.h"
374ca955 19#include "putilimp.h"
46f4442e
A
20#include "cstring.h"
21#include "olsontz.h"
22
23#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
b75a7d8f
A
24
25#define CASE(id,test) case id: \
26 name = #test; \
27 if (exec) { \
28 logln(#test "---"); logln(""); \
29 test(); \
30 } \
31 break
32
33// *****************************************************************************
34// class TimeZoneTest
35// *****************************************************************************
36
46f4442e
A
37// TODO: We should probably read following data at runtime, so we can update
38// the these values every release with necessary data changes.
39const int32_t TimeZoneTest::REFERENCE_YEAR = 2009;
40const char * TimeZoneTest::REFERENCE_DATA_VERSION = "2009d";
41
b75a7d8f
A
42void TimeZoneTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
43{
44 if (exec) logln("TestSuite TestTimeZone");
45 switch (index) {
46 CASE(0, TestPRTOffset);
47 CASE(1, TestVariousAPI518);
48 CASE(2, TestGetAvailableIDs913);
49 CASE(3, TestGenericAPI);
50 CASE(4, TestRuleAPI);
51 CASE(5, TestShortZoneIDs);
52 CASE(6, TestCustomParse);
53 CASE(7, TestDisplayName);
54 CASE(8, TestDSTSavings);
55 CASE(9, TestAlternateRules);
374ca955
A
56 CASE(10,TestCountries);
57 CASE(11,TestHistorical);
58 CASE(12,TestEquivalentIDs);
73c04bcf
A
59 CASE(13, TestAliasedNames);
60 CASE(14, TestFractionalDST);
46f4442e
A
61 CASE(15, TestFebruary);
62 CASE(16, TestCanonicalID);
63 CASE(17, TestDisplayNamesMeta);
374ca955 64 default: name = ""; break;
b75a7d8f
A
65 }
66}
67
68const int32_t TimeZoneTest::millisPerHour = 3600000;
69
70// ---------------------------------------------------------------------------------
71
72/**
73 * Generic API testing for API coverage.
74 */
75void
76TimeZoneTest::TestGenericAPI()
77{
78 UnicodeString id("NewGMT");
79 int32_t offset = 12345;
80
81 SimpleTimeZone *zone = new SimpleTimeZone(offset, id);
82 if (zone->useDaylightTime()) errln("FAIL: useDaylightTime should return FALSE");
83
84 TimeZone* zoneclone = zone->clone();
85 if (!(*zoneclone == *zone)) errln("FAIL: clone or operator== failed");
86 zoneclone->setID("abc");
87 if (!(*zoneclone != *zone)) errln("FAIL: clone or operator!= failed");
88 delete zoneclone;
89
90 zoneclone = zone->clone();
91 if (!(*zoneclone == *zone)) errln("FAIL: clone or operator== failed");
92 zoneclone->setRawOffset(45678);
93 if (!(*zoneclone != *zone)) errln("FAIL: clone or operator!= failed");
94
95 SimpleTimeZone copy(*zone);
96 if (!(copy == *zone)) errln("FAIL: copy constructor or operator== failed");
97 copy = *(SimpleTimeZone*)zoneclone;
98 if (!(copy == *zoneclone)) errln("FAIL: assignment operator or operator== failed");
99
100 TimeZone* saveDefault = TimeZone::createDefault();
374ca955 101 logln((UnicodeString)"TimeZone::createDefault() => " + saveDefault->getID(id));
46f4442e 102 TimeZone* pstZone = TimeZone::createTimeZone("America/Los_Angeles");
b75a7d8f 103
374ca955 104 logln("call uprv_timezone() which uses the host");
b75a7d8f
A
105 logln("to get the difference in seconds between coordinated universal");
106 logln("time and local time. E.g., -28,800 for PST (GMT-8hrs)");
107
108 int32_t tzoffset = uprv_timezone();
374ca955 109 logln(UnicodeString("Value returned from uprv_timezone = ") + tzoffset);
b75a7d8f
A
110 // Invert sign because UNIX semantics are backwards
111 if (tzoffset < 0)
112 tzoffset = -tzoffset;
46f4442e
A
113 if ((*saveDefault == *pstZone) && (tzoffset != 28800)) {
114 errln("FAIL: t_timezone may be incorrect. It is not 28800");
b75a7d8f 115 }
46f4442e
A
116
117 if ((tzoffset % 900) != 0) {
118 errln("FAIL: t_timezone may be incorrect. It is not a multiple of 15min. It is %d", tzoffset);
b75a7d8f
A
119 }
120
121 TimeZone::adoptDefault(zone);
122 TimeZone* defaultzone = TimeZone::createDefault();
123 if (defaultzone == zone ||
124 !(*defaultzone == *zone))
125 errln("FAIL: createDefault failed");
126 TimeZone::adoptDefault(saveDefault);
127 delete defaultzone;
128 delete zoneclone;
46f4442e
A
129 delete pstZone;
130
131 UErrorCode status = U_ZERO_ERROR;
132 const char* tzver = TimeZone::getTZDataVersion(status);
133 if (U_FAILURE(status)) {
134 errln("FAIL: getTZDataVersion failed");
135 } else if (uprv_strlen(tzver) != 5 /* 4 digits + 1 letter */) {
136 errln((UnicodeString)"FAIL: getTZDataVersion returned " + tzver);
137 } else {
138 logln((UnicodeString)"tzdata version: " + tzver);
139 }
b75a7d8f
A
140}
141
142// ---------------------------------------------------------------------------------
143
144/**
145 * Test the setStartRule/setEndRule API calls.
146 */
147void
148TimeZoneTest::TestRuleAPI()
149{
150 UErrorCode status = U_ZERO_ERROR;
151
152 UDate offset = 60*60*1000*1.75; // Pick a weird offset
153 SimpleTimeZone *zone = new SimpleTimeZone((int32_t)offset, "TestZone");
154 if (zone->useDaylightTime()) errln("FAIL: useDaylightTime should return FALSE");
155
156 // Establish our expected transition times. Do this with a non-DST
157 // calendar with the (above) declared local offset.
158 GregorianCalendar *gc = new GregorianCalendar(*zone, status);
159 if (failure(status, "new GregorianCalendar")) return;
160 gc->clear();
161 gc->set(1990, UCAL_MARCH, 1);
162 UDate marchOneStd = gc->getTime(status); // Local Std time midnight
163 gc->clear();
164 gc->set(1990, UCAL_JULY, 1);
165 UDate julyOneStd = gc->getTime(status); // Local Std time midnight
166 if (failure(status, "GregorianCalendar::getTime")) return;
167
168 // Starting and ending hours, WALL TIME
169 int32_t startHour = (int32_t)(2.25 * 3600000);
170 int32_t endHour = (int32_t)(3.5 * 3600000);
171
172 zone->setStartRule(UCAL_MARCH, 1, 0, startHour, status);
173 zone->setEndRule (UCAL_JULY, 1, 0, endHour, status);
174
175 delete gc;
176 gc = new GregorianCalendar(*zone, status);
177 if (failure(status, "new GregorianCalendar")) return;
178
179 UDate marchOne = marchOneStd + startHour;
180 UDate julyOne = julyOneStd + endHour - 3600000; // Adjust from wall to Std time
181
182 UDate expMarchOne = 636251400000.0;
183 if (marchOne != expMarchOne)
184 {
185 errln((UnicodeString)"FAIL: Expected start computed as " + marchOne +
186 " = " + dateToString(marchOne));
187 logln((UnicodeString)" Should be " + expMarchOne +
188 " = " + dateToString(expMarchOne));
189 }
190
191 UDate expJulyOne = 646793100000.0;
192 if (julyOne != expJulyOne)
193 {
194 errln((UnicodeString)"FAIL: Expected start computed as " + julyOne +
195 " = " + dateToString(julyOne));
196 logln((UnicodeString)" Should be " + expJulyOne +
197 " = " + dateToString(expJulyOne));
198 }
199
374ca955
A
200 testUsingBinarySearch(*zone, date(90, UCAL_JANUARY, 1), date(90, UCAL_JUNE, 15), marchOne);
201 testUsingBinarySearch(*zone, date(90, UCAL_JUNE, 1), date(90, UCAL_DECEMBER, 31), julyOne);
b75a7d8f
A
202
203 if (zone->inDaylightTime(marchOne - 1000, status) ||
204 !zone->inDaylightTime(marchOne, status))
205 errln("FAIL: Start rule broken");
206 if (!zone->inDaylightTime(julyOne - 1000, status) ||
207 zone->inDaylightTime(julyOne, status))
208 errln("FAIL: End rule broken");
209
210 zone->setStartYear(1991);
211 if (zone->inDaylightTime(marchOne, status) ||
212 zone->inDaylightTime(julyOne - 1000, status))
213 errln("FAIL: Start year broken");
214
215 failure(status, "TestRuleAPI");
216 delete gc;
217 delete zone;
218}
219
220void
374ca955
A
221TimeZoneTest::findTransition(const TimeZone& tz,
222 UDate min, UDate max) {
223 UErrorCode ec = U_ZERO_ERROR;
224 UnicodeString id,s;
225 UBool startsInDST = tz.inDaylightTime(min, ec);
226 if (failure(ec, "TimeZone::inDaylightTime")) return;
227 if (tz.inDaylightTime(max, ec) == startsInDST) {
228 logln("Error: " + tz.getID(id) + ".inDaylightTime(" + dateToString(min) + ") = " + (startsInDST?"TRUE":"FALSE") +
229 ", inDaylightTime(" + dateToString(max) + ") = " + (startsInDST?"TRUE":"FALSE"));
230 return;
231 }
232 if (failure(ec, "TimeZone::inDaylightTime")) return;
233 while ((max - min) > INTERVAL) {
234 UDate mid = (min + max) / 2;
235 if (tz.inDaylightTime(mid, ec) == startsInDST) {
236 min = mid;
237 } else {
238 max = mid;
239 }
240 if (failure(ec, "TimeZone::inDaylightTime")) return;
241 }
242 min = 1000.0 * uprv_floor(min/1000.0);
243 max = 1000.0 * uprv_floor(max/1000.0);
244 logln(tz.getID(id) + " Before: " + min/1000 + " = " +
245 dateToString(min,s,tz));
246 logln(tz.getID(id) + " After: " + max/1000 + " = " +
247 dateToString(max,s,tz));
248}
249
250void
251TimeZoneTest::testUsingBinarySearch(const TimeZone& tz,
252 UDate min, UDate max,
253 UDate expectedBoundary)
b75a7d8f
A
254{
255 UErrorCode status = U_ZERO_ERROR;
374ca955
A
256 UBool startsInDST = tz.inDaylightTime(min, status);
257 if (failure(status, "TimeZone::inDaylightTime")) return;
258 if (tz.inDaylightTime(max, status) == startsInDST) {
b75a7d8f
A
259 logln("Error: inDaylightTime(" + dateToString(max) + ") != " + ((!startsInDST)?"TRUE":"FALSE"));
260 return;
261 }
374ca955 262 if (failure(status, "TimeZone::inDaylightTime")) return;
b75a7d8f
A
263 while ((max - min) > INTERVAL) {
264 UDate mid = (min + max) / 2;
374ca955 265 if (tz.inDaylightTime(mid, status) == startsInDST) {
b75a7d8f 266 min = mid;
374ca955 267 } else {
b75a7d8f
A
268 max = mid;
269 }
374ca955 270 if (failure(status, "TimeZone::inDaylightTime")) return;
b75a7d8f
A
271 }
272 logln(UnicodeString("Binary Search Before: ") + uprv_floor(0.5 + min) + " = " + dateToString(min));
273 logln(UnicodeString("Binary Search After: ") + uprv_floor(0.5 + max) + " = " + dateToString(max));
274 UDate mindelta = expectedBoundary - min;
275 UDate maxdelta = max - expectedBoundary;
276 if (mindelta >= 0 &&
277 mindelta <= INTERVAL &&
278 maxdelta >= 0 &&
279 maxdelta <= INTERVAL)
280 logln(UnicodeString("PASS: Expected bdry: ") + expectedBoundary + " = " + dateToString(expectedBoundary));
281 else
282 errln(UnicodeString("FAIL: Expected bdry: ") + expectedBoundary + " = " + dateToString(expectedBoundary));
283}
284
285const UDate TimeZoneTest::INTERVAL = 100;
286
287// ---------------------------------------------------------------------------------
288
289// -------------------------------------
290
291/**
292 * Test the offset of the PRT timezone.
293 */
294void
295TimeZoneTest::TestPRTOffset()
296{
297 TimeZone* tz = TimeZone::createTimeZone("PRT");
298 if (tz == 0) {
299 errln("FAIL: TimeZone(PRT) is null");
300 }
301 else {
374ca955
A
302 int32_t expectedHour = -4;
303 double expectedOffset = (((double)expectedHour) * millisPerHour);
304 double foundOffset = tz->getRawOffset();
305 int32_t foundHour = (int32_t)foundOffset / millisPerHour;
306 if (expectedOffset != foundOffset) {
307 errln("FAIL: Offset for PRT should be %d, found %d", expectedHour, foundHour);
308 } else {
309 logln("PASS: Offset for PRT should be %d, found %d", expectedHour, foundHour);
310 }
b75a7d8f
A
311 }
312 delete tz;
313}
314
315// -------------------------------------
316
317/**
318 * Regress a specific bug with a sequence of API calls.
319 */
320void
321TimeZoneTest::TestVariousAPI518()
322{
323 UErrorCode status = U_ZERO_ERROR;
324 TimeZone* time_zone = TimeZone::createTimeZone("PST");
325 UDate d = date(97, UCAL_APRIL, 30);
326 UnicodeString str;
327 logln("The timezone is " + time_zone->getID(str));
328 if (!time_zone->inDaylightTime(d, status)) errln("FAIL: inDaylightTime returned FALSE");
329 if (U_FAILURE(status)) { errln("FAIL: TimeZone::inDaylightTime failed"); return; }
330 if (!time_zone->useDaylightTime()) errln("FAIL: useDaylightTime returned FALSE");
331 if (time_zone->getRawOffset() != - 8 * millisPerHour) errln("FAIL: getRawOffset returned wrong value");
332 GregorianCalendar *gc = new GregorianCalendar(status);
333 if (U_FAILURE(status)) { errln("FAIL: Couldn't create GregorianCalendar"); return; }
334 gc->setTime(d, status);
335 if (U_FAILURE(status)) { errln("FAIL: GregorianCalendar::setTime failed"); return; }
336 if (time_zone->getOffset(gc->AD, gc->get(UCAL_YEAR, status), gc->get(UCAL_MONTH, status),
337 gc->get(UCAL_DATE, status), (uint8_t)gc->get(UCAL_DAY_OF_WEEK, status), 0, status) != - 7 * millisPerHour)
338 errln("FAIL: getOffset returned wrong value");
339 if (U_FAILURE(status)) { errln("FAIL: GregorianCalendar::set failed"); return; }
340 delete gc;
341 delete time_zone;
342}
343
344// -------------------------------------
345
346/**
347 * Test the call which retrieves the available IDs.
348 */
349void
350TimeZoneTest::TestGetAvailableIDs913()
351{
352 UErrorCode ec = U_ZERO_ERROR;
374ca955
A
353 int32_t i;
354
355#ifdef U_USE_TIMEZONE_OBSOLETE_2_8
356 // Test legacy API -- remove these tests when the corresponding API goes away (duh)
357 int32_t numIDs = -1;
358 const UnicodeString** ids = TimeZone::createAvailableIDs(numIDs);
359 if (ids == 0 || numIDs < 1) {
360 errln("FAIL: createAvailableIDs()");
361 } else {
362 UnicodeString buf("TimeZone::createAvailableIDs() = { ");
363 for(i=0; i<numIDs; ++i) {
364 if (i) buf.append(", ");
365 buf.append(*ids[i]);
366 }
367 buf.append(" } ");
368 logln(buf + numIDs);
369 // we own the array; the caller owns the contained strings (yuck)
370 uprv_free(ids);
371 }
372
373 numIDs = -1;
374 ids = TimeZone::createAvailableIDs(-8*U_MILLIS_PER_HOUR, numIDs);
375 if (ids == 0 || numIDs < 1) {
376 errln("FAIL: createAvailableIDs(-8:00)");
377 } else {
378 UnicodeString buf("TimeZone::createAvailableIDs(-8:00) = { ");
379 for(i=0; i<numIDs; ++i) {
380 if (i) buf.append(", ");
381 buf.append(*ids[i]);
382 }
383 buf.append(" } ");
384 logln(buf + numIDs);
385 // we own the array; the caller owns the contained strings (yuck)
386 uprv_free(ids);
387 }
388 numIDs = -1;
389 ids = TimeZone::createAvailableIDs("US", numIDs);
390 if (ids == 0 || numIDs < 1) {
391 errln("FAIL: createAvailableIDs(US) ids=%d, numIDs=%d", ids, numIDs);
392 } else {
393 UnicodeString buf("TimeZone::createAvailableIDs(US) = { ");
394 for(i=0; i<numIDs; ++i) {
395 if (i) buf.append(", ");
396 buf.append(*ids[i]);
397 }
398 buf.append(" } ");
399 logln(buf + numIDs);
400 // we own the array; the caller owns the contained strings (yuck)
401 uprv_free(ids);
402 }
403#endif
404
b75a7d8f
A
405 UnicodeString str;
406 UnicodeString *buf = new UnicodeString("TimeZone::createEnumeration() = { ");
407 int32_t s_length;
408 StringEnumeration* s = TimeZone::createEnumeration();
409 s_length = s->count(ec);
b75a7d8f
A
410 for (i = 0; i < s_length;++i) {
411 if (i > 0) *buf += ", ";
374ca955
A
412 if ((i & 1) == 0) {
413 *buf += *s->snext(ec);
414 } else {
415 *buf += UnicodeString(s->next(NULL, ec), "");
416 }
417
418 if((i % 5) == 4) {
419 // replace s with a clone of itself
420 StringEnumeration *s2 = s->clone();
421 if(s2 == NULL || s_length != s2->count(ec)) {
422 errln("TimezoneEnumeration.clone() failed");
423 } else {
424 delete s;
425 s = s2;
426 }
427 }
b75a7d8f
A
428 }
429 *buf += " };";
430 logln(*buf);
431
432 /* Confirm that the following zones can be retrieved: The first
433 * zone, the last zone, and one in-between. This tests the binary
434 * search through the system zone data.
435 */
436 s->reset(ec);
437 int32_t middle = s_length/2;
438 for (i=0; i<s_length; ++i) {
439 const UnicodeString* id = s->snext(ec);
440 if (i==0 || i==middle || i==(s_length-1)) {
441 TimeZone *z = TimeZone::createTimeZone(*id);
442 if (z == 0) {
443 errln(UnicodeString("FAIL: createTimeZone(") +
444 *id + ") -> 0");
445 } else if (z->getID(str) != *id) {
446 errln(UnicodeString("FAIL: createTimeZone(") +
447 *id + ") -> zone " + str);
448 } else {
449 logln(UnicodeString("OK: createTimeZone(") +
450 *id + ") succeeded");
451 }
452 delete z;
453 }
454 }
455 delete s;
456
457 buf->truncate(0);
374ca955
A
458 *buf += "TimeZone::createEnumeration(GMT+01:00) = { ";
459
460 s = TimeZone::createEnumeration(1 * U_MILLIS_PER_HOUR);
461 s_length = s->count(ec);
462 for (i = 0; i < s_length;++i) {
463 if (i > 0) *buf += ", ";
464 *buf += *s->snext(ec);
465 }
466 delete s;
467 *buf += " };";
468 logln(*buf);
469
470
471 buf->truncate(0);
472 *buf += "TimeZone::createEnumeration(US) = { ";
b75a7d8f 473
374ca955 474 s = TimeZone::createEnumeration("US");
b75a7d8f
A
475 s_length = s->count(ec);
476 for (i = 0; i < s_length;++i) {
477 if (i > 0) *buf += ", ";
478 *buf += *s->snext(ec);
479 }
480 *buf += " };";
481 logln(*buf);
374ca955 482
b75a7d8f
A
483 TimeZone *tz = TimeZone::createTimeZone("PST");
484 if (tz != 0) logln("getTimeZone(PST) = " + tz->getID(str));
485 else errln("FAIL: getTimeZone(PST) = null");
486 delete tz;
487 tz = TimeZone::createTimeZone("America/Los_Angeles");
488 if (tz != 0) logln("getTimeZone(America/Los_Angeles) = " + tz->getID(str));
489 else errln("FAIL: getTimeZone(PST) = null");
490 delete tz;
491
492 // @bug 4096694
493 tz = TimeZone::createTimeZone("NON_EXISTENT");
494 UnicodeString temp;
495 if (tz == 0)
496 errln("FAIL: getTimeZone(NON_EXISTENT) = null");
497 else if (tz->getID(temp) != "GMT")
498 errln("FAIL: getTimeZone(NON_EXISTENT) = " + temp);
499 delete tz;
500
501 delete buf;
502 delete s;
503}
504
505
506/**
374ca955
A
507 * NOTE: As of ICU 2.8, this test confirms that the "tz.alias"
508 * file, used to build ICU alias zones, is working. It also
509 * looks at some genuine Olson compatibility IDs. [aliu]
510 *
b75a7d8f
A
511 * This test is problematic. It should really just confirm that
512 * the list of compatibility zone IDs exist and are somewhat
513 * meaningful (that is, they aren't all aliases of GMT). It goes a
514 * bit further -- it hard-codes expectations about zone behavior,
515 * when in fact zones are redefined quite frequently. ICU's build
516 * process means that it is easy to update ICU to contain the
517 * latest Olson zone data, but if a zone tested here changes, then
518 * this test will fail. I have updated the test for 1999j data,
519 * but further updates will probably be required. Note that some
520 * of the concerts listed below no longer apply -- in particular,
521 * we do NOT overwrite real UNIX zones with 3-letter IDs. There
522 * are two points of overlap as of 1999j: MET and EET. These are
523 * both real UNIX zones, so we just use the official
524 * definition. This test has been updated to reflect this.
525 * 12/3/99 aliu
526 *
4162bf98
A
527 * Added tests for additional zones and aliases from the icuzones file.
528 * Markus Scherer 2006-nov-06
529 *
b75a7d8f
A
530 * [srl - from java - 7/5/1998]
531 * @bug 4130885
532 * Certain short zone IDs, used since 1.1.x, are incorrect.
533 *
534 * The worst of these is:
535 *
536 * "CAT" (Central African Time) should be GMT+2:00, but instead returns a
537 * zone at GMT-1:00. The zone at GMT-1:00 should be called EGT, CVT, EGST,
538 * or AZOST, depending on which zone is meant, but in no case is it CAT.
539 *
540 * Other wrong zone IDs:
541 *
542 * ECT (European Central Time) GMT+1:00: ECT is Ecuador Time,
543 * GMT-5:00. European Central time is abbreviated CEST.
544 *
545 * SST (Solomon Island Time) GMT+11:00. SST is actually Samoa Standard Time,
546 * GMT-11:00. Solomon Island time is SBT.
547 *
548 * NST (New Zealand Time) GMT+12:00. NST is the abbreviation for
549 * Newfoundland Standard Time, GMT-3:30. New Zealanders use NZST.
550 *
551 * AST (Alaska Standard Time) GMT-9:00. [This has already been noted in
552 * another bug.] It should be "AKST". AST is Atlantic Standard Time,
553 * GMT-4:00.
554 *
555 * PNT (Phoenix Time) GMT-7:00. PNT usually means Pitcairn Time,
556 * GMT-8:30. There is no standard abbreviation for Phoenix time, as distinct
557 * from MST with daylight savings.
558 *
559 * In addition to these problems, a number of zones are FAKE. That is, they
560 * don't match what people use in the real world.
561 *
562 * FAKE zones:
563 *
564 * EET (should be EEST)
565 * ART (should be EEST)
566 * MET (should be IRST)
567 * NET (should be AMST)
568 * PLT (should be PKT)
569 * BST (should be BDT)
570 * VST (should be ICT)
571 * CTT (should be CST) +
572 * ACT (should be CST) +
573 * AET (should be EST) +
574 * MIT (should be WST) +
575 * IET (should be EST) +
576 * PRT (should be AST) +
577 * CNT (should be NST)
578 * AGT (should be ARST)
579 * BET (should be EST) +
580 *
581 * + A zone with the correct name already exists and means something
582 * else. E.g., EST usually indicates the US Eastern zone, so it cannot be
583 * used for Brazil (BET).
584 */
585void TimeZoneTest::TestShortZoneIDs()
586{
46f4442e
A
587 UErrorCode status = U_ZERO_ERROR;
588
589 // This test case is tzdata version sensitive.
590 UBool isNonReferenceTzdataVersion = FALSE;
591 const char *tzdataVer = TimeZone::getTZDataVersion(status);
592 if (failure(status, "getTZDataVersion")) return;
593 if (uprv_strcmp(tzdataVer, TimeZoneTest::REFERENCE_DATA_VERSION) != 0) {
594 // Note: We want to display a warning message here if
595 // REFERENCE_DATA_VERSION is out of date - so we
596 // do not forget to update the value before GA.
597 isNonReferenceTzdataVersion = TRUE;
598 logln(UnicodeString("Warning: Active tzdata version (") + tzdataVer +
599 ") does not match the reference tzdata version ("
600 + REFERENCE_DATA_VERSION + ") for this test case data.");
601 }
602
603 // Note: useDaylightTime returns true if DST is observed
604 // in the time zone in the current calendar year. The test
605 // data is valid for the date after the reference year below.
606 // If system clock is before the year, some test cases may
607 // fail.
608 GregorianCalendar cal(*TimeZone::getGMT(), status);
609 if (failure(status, "GregorianCalendar")) return;
610 cal.set(TimeZoneTest::REFERENCE_YEAR, UCAL_JANUARY, 2); // day 2 in GMT
611
612 UBool isDateBeforeReferenceYear = ucal_getNow() < cal.getTime(status);
613 if (failure(status, "Calendar::getTime")) return;
614 if (isDateBeforeReferenceYear) {
615 logln("Warning: Past time is set to the system clock. Some test cases may not return expected results.");
616 }
617
b75a7d8f
A
618 int32_t i;
619 // Create a small struct to hold the array
620 struct
621 {
622 const char *id;
623 int32_t offset;
624 UBool daylight;
625 }
626 kReferenceList [] =
627 {
628 {"MIT", -660, FALSE},
629 {"HST", -600, FALSE},
630 {"AST", -540, TRUE},
631 {"PST", -480, TRUE},
632 {"PNT", -420, FALSE},
374ca955 633 {"MST", -420, FALSE}, // updated Aug 2003 aliu
b75a7d8f 634 {"CST", -360, TRUE},
73c04bcf 635 {"IET", -300, TRUE}, // updated Jan 2006 srl
374ca955 636 {"EST", -300, FALSE}, // updated Aug 2003 aliu
b75a7d8f
A
637 {"PRT", -240, FALSE},
638 {"CNT", -210, TRUE},
4162bf98 639 {"AGT", -180, TRUE}, // updated by tzdata2007k
b75a7d8f 640 {"BET", -180, TRUE},
b75a7d8f
A
641 {"GMT", 0, FALSE},
642 {"UTC", 0, FALSE}, // ** srl: seems broken in C++
643 {"ECT", 60, TRUE},
46f4442e 644 {"MET", 60, TRUE}, // updated 12/3/99 aliu
b75a7d8f
A
645 {"ART", 120, TRUE},
646 {"EET", 120, TRUE},
46f4442e 647 {"CAT", 120, FALSE}, // Africa/Harare
b75a7d8f 648 {"EAT", 180, FALSE},
b75a7d8f 649 {"NET", 240, TRUE}, // updated 12/3/99 aliu
46f4442e 650 {"PLT", 300, FALSE}, // updated by 2008c - no DST after 2008
b75a7d8f
A
651 {"IST", 330, FALSE},
652 {"BST", 360, FALSE},
653 {"VST", 420, FALSE},
374ca955 654 {"CTT", 480, FALSE}, // updated Aug 2003 aliu
b75a7d8f 655 {"JST", 540, FALSE},
374ca955 656 {"ACT", 570, FALSE}, // updated Aug 2003 aliu
b75a7d8f
A
657 {"AET", 600, TRUE},
658 {"SST", 660, FALSE},
b75a7d8f 659 {"NST", 720, TRUE}, // Pacific/Auckland
46f4442e
A
660
661 // From icuzones:
662 {"Etc/Unknown", 0, FALSE},
663
664 {"SystemV/AST4ADT", -240, TRUE},
665 {"SystemV/EST5EDT", -300, TRUE},
666 {"SystemV/CST6CDT", -360, TRUE},
667 {"SystemV/MST7MDT", -420, TRUE},
668 {"SystemV/PST8PDT", -480, TRUE},
669 {"SystemV/YST9YDT", -540, TRUE},
670 {"SystemV/AST4", -240, FALSE},
671 {"SystemV/EST5", -300, FALSE},
672 {"SystemV/CST6", -360, FALSE},
673 {"SystemV/MST7", -420, FALSE},
674 {"SystemV/PST8", -480, FALSE},
675 {"SystemV/YST9", -540, FALSE},
676 {"SystemV/HST10", -600, FALSE},
677
b75a7d8f
A
678 {"",0,FALSE}
679 };
680
b75a7d8f
A
681 for(i=0;kReferenceList[i].id[0];i++) {
682 UnicodeString itsID(kReferenceList[i].id);
683 UBool ok = TRUE;
684 // Check existence.
685 TimeZone *tz = TimeZone::createTimeZone(itsID);
46f4442e 686 if (!tz || (kReferenceList[i].offset != 0 && *tz == *TimeZone::getGMT())) {
b75a7d8f
A
687 errln("FAIL: Time Zone " + itsID + " does not exist!");
688 continue;
689 }
690
691 // Check daylight usage.
692 UBool usesDaylight = tz->useDaylightTime();
693 if (usesDaylight != kReferenceList[i].daylight) {
46f4442e
A
694 if (isNonReferenceTzdataVersion || isDateBeforeReferenceYear) {
695 logln("Warning: Time Zone " + itsID + " use daylight is " +
696 (usesDaylight?"TRUE":"FALSE") +
697 " but it should be " +
698 ((kReferenceList[i].daylight)?"TRUE":"FALSE"));
699 } else {
700 errln("FAIL: Time Zone " + itsID + " use daylight is " +
701 (usesDaylight?"TRUE":"FALSE") +
702 " but it should be " +
703 ((kReferenceList[i].daylight)?"TRUE":"FALSE"));
704 }
b75a7d8f
A
705 ok = FALSE;
706 }
707
708 // Check offset
709 int32_t offsetInMinutes = tz->getRawOffset()/60000;
710 if (offsetInMinutes != kReferenceList[i].offset) {
46f4442e
A
711 if (isNonReferenceTzdataVersion || isDateBeforeReferenceYear) {
712 logln("FAIL: Time Zone " + itsID + " raw offset is " +
713 offsetInMinutes +
714 " but it should be " + kReferenceList[i].offset);
715 } else {
716 errln("FAIL: Time Zone " + itsID + " raw offset is " +
717 offsetInMinutes +
718 " but it should be " + kReferenceList[i].offset);
719 }
b75a7d8f
A
720 ok = FALSE;
721 }
722
723 if (ok) {
724 logln("OK: " + itsID +
725 " useDaylightTime() & getRawOffset() as expected");
726 }
727 delete tz;
728 }
729
730
731 // OK now test compat
732 logln("Testing for compatibility zones");
733
734 const char* compatibilityMap[] = {
735 // This list is copied from tz.alias. If tz.alias
4162bf98 736 // changes, this list must be updated. Current as of Mar 2007
b75a7d8f
A
737 "ACT", "Australia/Darwin",
738 "AET", "Australia/Sydney",
739 "AGT", "America/Buenos_Aires",
740 "ART", "Africa/Cairo",
741 "AST", "America/Anchorage",
742 "BET", "America/Sao_Paulo",
4162bf98 743 "BST", "Asia/Dhaka", // # spelling changed in 2000h; was Asia/Dacca
b75a7d8f
A
744 "CAT", "Africa/Harare",
745 "CNT", "America/St_Johns",
746 "CST", "America/Chicago",
747 "CTT", "Asia/Shanghai",
748 "EAT", "Africa/Addis_Ababa",
749 "ECT", "Europe/Paris",
750 // EET Europe/Istanbul # EET is a standard UNIX zone
4162bf98
A
751 // "EST", "America/New_York", # Defined as -05:00
752 // "HST", "Pacific/Honolulu", # Defined as -10:00
b75a7d8f
A
753 "IET", "America/Indianapolis",
754 "IST", "Asia/Calcutta",
755 "JST", "Asia/Tokyo",
756 // MET Asia/Tehran # MET is a standard UNIX zone
757 "MIT", "Pacific/Apia",
4162bf98 758 // "MST", "America/Denver", # Defined as -07:00
b75a7d8f
A
759 "NET", "Asia/Yerevan",
760 "NST", "Pacific/Auckland",
761 "PLT", "Asia/Karachi",
762 "PNT", "America/Phoenix",
763 "PRT", "America/Puerto_Rico",
764 "PST", "America/Los_Angeles",
765 "SST", "Pacific/Guadalcanal",
766 "UTC", "Etc/GMT",
767 "VST", "Asia/Saigon",
768 "","",""
769 };
770
771 for (i=0;*compatibilityMap[i];i+=2) {
772 UnicodeString itsID;
773
774 const char *zone1 = compatibilityMap[i];
775 const char *zone2 = compatibilityMap[i+1];
776
777 TimeZone *tz1 = TimeZone::createTimeZone(zone1);
778 TimeZone *tz2 = TimeZone::createTimeZone(zone2);
779
780 if (!tz1) {
781 errln(UnicodeString("FAIL: Could not find short ID zone ") + zone1);
782 }
783 if (!tz2) {
784 errln(UnicodeString("FAIL: Could not find long ID zone ") + zone2);
785 }
786
787 if (tz1 && tz2) {
788 // make NAME same so comparison will only look at the rest
789 tz2->setID(tz1->getID(itsID));
790
791 if (*tz1 != *tz2) {
792 errln("FAIL: " + UnicodeString(zone1) +
793 " != " + UnicodeString(zone2));
794 } else {
795 logln("OK: " + UnicodeString(zone1) +
796 " == " + UnicodeString(zone2));
797 }
798 }
799
800 delete tz1;
801 delete tz2;
802 }
803}
804
46f4442e 805
b75a7d8f
A
806/**
807 * Utility function for TestCustomParse
808 */
46f4442e
A
809UnicodeString& TimeZoneTest::formatOffset(int32_t offset, UnicodeString &rv) {
810 rv.remove();
811 UChar sign = 0x002B;
812 if (offset < 0) {
813 sign = 0x002D;
814 offset = -offset;
815 }
816
817 int32_t s = offset % 60;
818 offset /= 60;
819 int32_t m = offset % 60;
820 int32_t h = offset / 60;
821
822 rv += (UChar)(sign);
823 if (h >= 10) {
824 rv += (UChar)(0x0030 + (h/10));
825 } else {
826 rv += (UChar)0x0030;
827 }
828 rv += (UChar)(0x0030 + (h%10));
829
830 rv += (UChar)0x003A; /* ':' */
831 if (m >= 10) {
832 rv += (UChar)(0x0030 + (m/10));
833 } else {
834 rv += (UChar)0x0030;
835 }
836 rv += (UChar)(0x0030 + (m%10));
b75a7d8f 837
46f4442e
A
838 if (s) {
839 rv += (UChar)0x003A; /* ':' */
840 if (s >= 10) {
841 rv += (UChar)(0x0030 + (s/10));
842 } else {
843 rv += (UChar)0x0030;
844 }
845 rv += (UChar)(0x0030 + (s%10));
846 }
847 return rv;
848}
b75a7d8f 849
46f4442e
A
850/**
851 * Utility function for TestCustomParse, generating time zone ID
852 * string for the give offset.
853 */
854UnicodeString& TimeZoneTest::formatTZID(int32_t offset, UnicodeString &rv) {
855 rv.remove();
856 UChar sign = 0x002B;
857 if (offset < 0) {
858 sign = 0x002D;
859 offset = -offset;
860 }
b75a7d8f 861
46f4442e
A
862 int32_t s = offset % 60;
863 offset /= 60;
864 int32_t m = offset % 60;
865 int32_t h = offset / 60;
b75a7d8f 866
46f4442e
A
867 rv += "GMT";
868 rv += (UChar)(sign);
869 if (h >= 10) {
870 rv += (UChar)(0x0030 + (h/10));
871 } else {
872 rv += (UChar)0x0030;
873 }
874 rv += (UChar)(0x0030 + (h%10));
b75a7d8f 875
46f4442e
A
876 if (m >= 10) {
877 rv += (UChar)(0x0030 + (m/10));
878 } else {
879 rv += (UChar)0x0030;
880 }
881 rv += (UChar)(0x0030 + (m%10));
b75a7d8f 882
46f4442e
A
883 if (s) {
884 if (s >= 10) {
885 rv += (UChar)(0x0030 + (s/10));
886 } else {
887 rv += (UChar)0x0030;
888 }
889 rv += (UChar)(0x0030 + (s%10));
890 }
891 return rv;
b75a7d8f
A
892}
893
b75a7d8f
A
894/**
895 * As part of the VM fix (see CCC approved RFE 4028006, bug
896 * 4044013), TimeZone.getTimeZone() has been modified to recognize
897 * generic IDs of the form GMT[+-]hh:mm, GMT[+-]hhmm, and
898 * GMT[+-]hh. Test this behavior here.
899 *
900 * @bug 4044013
901 */
902void TimeZoneTest::TestCustomParse()
903{
904 int32_t i;
905 const int32_t kUnparseable = 604800; // the number of seconds in a week. More than any offset should be.
b75a7d8f
A
906
907 struct
908 {
909 const char *customId;
910 int32_t expectedOffset;
911 }
912 kData[] =
913 {
46f4442e
A
914 // ID Expected offset in seconds
915 {"GMT", kUnparseable}, //Isn't custom. [returns normal GMT]
b75a7d8f 916 {"GMT-YOUR.AD.HERE", kUnparseable},
46f4442e
A
917 {"GMT0", kUnparseable},
918 {"GMT+0", (0)},
919 {"GMT+1", (1*60*60)},
920 {"GMT-0030", (-30*60)},
921 {"GMT+15:99", kUnparseable},
b75a7d8f
A
922 {"GMT+", kUnparseable},
923 {"GMT-", kUnparseable},
924 {"GMT+0:", kUnparseable},
925 {"GMT-:", kUnparseable},
46f4442e
A
926 {"GMT-YOUR.AD.HERE", kUnparseable},
927 {"GMT+0010", (10*60)}, // Interpret this as 00:10
928 {"GMT-10", (-10*60*60)},
929 {"GMT+30", kUnparseable},
930 {"GMT-3:30", (-(3*60+30)*60)},
931 {"GMT-230", (-(2*60+30)*60)},
932 {"GMT+05:13:05", ((5*60+13)*60+5)},
933 {"GMT-71023", (-((7*60+10)*60+23))},
934 {"GMT+01:23:45:67", kUnparseable},
935 {"GMT+01:234", kUnparseable},
936 {"GMT-2:31:123", kUnparseable},
937 {"GMT+3:75", kUnparseable},
938 {"GMT-01010101", kUnparseable},
b75a7d8f
A
939 {0, 0}
940 };
941
46f4442e 942 for (i=0; kData[i].customId != 0; i++) {
b75a7d8f
A
943 UnicodeString id(kData[i].customId);
944 int32_t exp = kData[i].expectedOffset;
b75a7d8f
A
945 TimeZone *zone = TimeZone::createTimeZone(id);
946 UnicodeString itsID, temp;
947
46f4442e
A
948 if (zone->getDynamicClassID() == OlsonTimeZone::getStaticClassID()) {
949 logln(id + " -> Olson time zone");
950 } else {
b75a7d8f 951 zone->getID(itsID);
46f4442e
A
952 int32_t ioffset = zone->getRawOffset()/1000;
953 UnicodeString offset, expectedID;
954 formatOffset(ioffset, offset);
955 formatTZID(ioffset, expectedID);
956 logln(id + " -> " + itsID + " " + offset);
957 if (exp == kUnparseable && itsID != "GMT") {
958 errln("Expected parse failure for " + id +
959 ", got offset of " + offset +
960 ", id " + itsID);
b75a7d8f 961 }
46f4442e
A
962 // JDK 1.3 creates custom zones with the ID "Custom"
963 // JDK 1.4 creates custom zones with IDs of the form "GMT+02:00"
964 // ICU creates custom zones with IDs of the form "GMT+0200"
965 else if (exp != kUnparseable && (ioffset != exp || itsID != expectedID)) {
966 errln("Expected offset of " + formatOffset(exp, temp) +
967 ", id " + expectedID +
968 ", for " + id +
969 ", got offset of " + offset +
970 ", id " + itsID);
b75a7d8f
A
971 }
972 }
973 delete zone;
974 }
975}
976
73c04bcf
A
977void
978TimeZoneTest::TestAliasedNames()
979{
980 struct {
981 const char *from;
982 const char *to;
983 } kData[] = {
984 /* Generated by org.unicode.cldr.tool.CountItems */
985
986 /* zoneID, canonical zoneID */
987 {"Africa/Timbuktu", "Africa/Bamako"},
988 {"America/Argentina/Buenos_Aires", "America/Buenos_Aires"},
989 {"America/Argentina/Catamarca", "America/Catamarca"},
990 {"America/Argentina/ComodRivadavia", "America/Catamarca"},
991 {"America/Argentina/Cordoba", "America/Cordoba"},
992 {"America/Argentina/Jujuy", "America/Jujuy"},
993 {"America/Argentina/Mendoza", "America/Mendoza"},
994 {"America/Atka", "America/Adak"},
995 {"America/Ensenada", "America/Tijuana"},
996 {"America/Fort_Wayne", "America/Indiana/Indianapolis"},
997 {"America/Indianapolis", "America/Indiana/Indianapolis"},
998 {"America/Knox_IN", "America/Indiana/Knox"},
999 {"America/Louisville", "America/Kentucky/Louisville"},
1000 {"America/Porto_Acre", "America/Rio_Branco"},
1001 {"America/Rosario", "America/Cordoba"},
1002 {"America/Virgin", "America/St_Thomas"},
1003 {"Asia/Ashkhabad", "Asia/Ashgabat"},
1004 {"Asia/Chungking", "Asia/Chongqing"},
1005 {"Asia/Dacca", "Asia/Dhaka"},
1006 {"Asia/Istanbul", "Europe/Istanbul"},
1007 {"Asia/Macao", "Asia/Macau"},
1008 {"Asia/Tel_Aviv", "Asia/Jerusalem"},
1009 {"Asia/Thimbu", "Asia/Thimphu"},
1010 {"Asia/Ujung_Pandang", "Asia/Makassar"},
1011 {"Asia/Ulan_Bator", "Asia/Ulaanbaatar"},
1012 {"Australia/ACT", "Australia/Sydney"},
1013 {"Australia/Canberra", "Australia/Sydney"},
1014 {"Australia/LHI", "Australia/Lord_Howe"},
1015 {"Australia/NSW", "Australia/Sydney"},
1016 {"Australia/North", "Australia/Darwin"},
1017 {"Australia/Queensland", "Australia/Brisbane"},
1018 {"Australia/South", "Australia/Adelaide"},
1019 {"Australia/Tasmania", "Australia/Hobart"},
1020 {"Australia/Victoria", "Australia/Melbourne"},
1021 {"Australia/West", "Australia/Perth"},
1022 {"Australia/Yancowinna", "Australia/Broken_Hill"},
1023 {"Brazil/Acre", "America/Rio_Branco"},
1024 {"Brazil/DeNoronha", "America/Noronha"},
1025 {"Brazil/East", "America/Sao_Paulo"},
1026 {"Brazil/West", "America/Manaus"},
1027 {"Canada/Atlantic", "America/Halifax"},
1028 {"Canada/Central", "America/Winnipeg"},
1029 {"Canada/East-Saskatchewan", "America/Regina"},
1030 {"Canada/Eastern", "America/Toronto"},
1031 {"Canada/Mountain", "America/Edmonton"},
1032 {"Canada/Newfoundland", "America/St_Johns"},
1033 {"Canada/Pacific", "America/Vancouver"},
1034 {"Canada/Saskatchewan", "America/Regina"},
1035 {"Canada/Yukon", "America/Whitehorse"},
1036 {"Chile/Continental", "America/Santiago"},
1037 {"Chile/EasterIsland", "Pacific/Easter"},
1038 {"Cuba", "America/Havana"},
1039 {"Egypt", "Africa/Cairo"},
1040 {"Eire", "Europe/Dublin"},
1041 {"Etc/GMT+0", "Etc/GMT"},
1042 {"Etc/GMT-0", "Etc/GMT"},
1043 {"Etc/GMT0", "Etc/GMT"},
1044 {"Etc/Greenwich", "Etc/GMT"},
1045 {"Etc/UCT", "Etc/GMT"},
1046 {"Etc/UTC", "Etc/GMT"},
1047 {"Etc/Universal", "Etc/GMT"},
1048 {"Etc/Zulu", "Etc/GMT"},
1049 {"Europe/Belfast", "Europe/London"},
1050 {"Europe/Nicosia", "Asia/Nicosia"},
1051 {"Europe/Tiraspol", "Europe/Chisinau"},
1052 {"GB", "Europe/London"},
1053 {"GB-Eire", "Europe/London"},
1054 {"GMT", "Etc/GMT"},
1055 {"GMT+0", "Etc/GMT"},
1056 {"GMT-0", "Etc/GMT"},
1057 {"GMT0", "Etc/GMT"},
1058 {"Greenwich", "Etc/GMT"},
1059 {"Hongkong", "Asia/Hong_Kong"},
1060 {"Iceland", "Atlantic/Reykjavik"},
1061 {"Iran", "Asia/Tehran"},
1062 {"Israel", "Asia/Jerusalem"},
1063 {"Jamaica", "America/Jamaica"},
1064 {"Japan", "Asia/Tokyo"},
1065 {"Kwajalein", "Pacific/Kwajalein"},
1066 {"Libya", "Africa/Tripoli"},
1067 {"Mexico/BajaNorte", "America/Tijuana"},
1068 {"Mexico/BajaSur", "America/Mazatlan"},
1069 {"Mexico/General", "America/Mexico_City"},
1070 {"NZ", "Pacific/Auckland"},
1071 {"NZ-CHAT", "Pacific/Chatham"},
1072 {"Navajo", "America/Shiprock"},
1073 {"PRC", "Asia/Shanghai"},
1074 {"Pacific/Samoa", "Pacific/Pago_Pago"},
1075 {"Pacific/Yap", "Pacific/Truk"},
1076 {"Poland", "Europe/Warsaw"},
1077 {"Portugal", "Europe/Lisbon"},
1078 {"ROC", "Asia/Taipei"},
1079 {"ROK", "Asia/Seoul"},
1080 {"Singapore", "Asia/Singapore"},
1081 {"Turkey", "Europe/Istanbul"},
1082 {"UCT", "Etc/GMT"},
1083 {"US/Alaska", "America/Anchorage"},
1084 {"US/Aleutian", "America/Adak"},
1085 {"US/Arizona", "America/Phoenix"},
1086 {"US/Central", "America/Chicago"},
1087 {"US/East-Indiana", "America/Indiana/Indianapolis"},
1088 {"US/Eastern", "America/New_York"},
1089 {"US/Hawaii", "Pacific/Honolulu"},
1090 {"US/Indiana-Starke", "America/Indiana/Knox"},
1091 {"US/Michigan", "America/Detroit"},
1092 {"US/Mountain", "America/Denver"},
1093 {"US/Pacific", "America/Los_Angeles"},
1094 {"US/Pacific-New", "America/Los_Angeles"},
1095 {"US/Samoa", "Pacific/Pago_Pago"},
1096 {"UTC", "Etc/GMT"},
1097 {"Universal", "Etc/GMT"},
1098 {"W-SU", "Europe/Moscow"},
1099 {"Zulu", "Etc/GMT"},
1100 /* Total: 113 */
1101
1102 };
1103
1104 TimeZone::EDisplayType styles[] = { TimeZone::SHORT, TimeZone::LONG };
1105 UBool useDst[] = { FALSE, TRUE };
1106 int32_t noLoc = uloc_countAvailable();
1107
73c04bcf
A
1108 int32_t i, j, k, loc;
1109 UnicodeString fromName, toName;
1110 TimeZone *from = NULL, *to = NULL;
1111 for(i = 0; i < (int32_t)(sizeof(kData)/sizeof(kData[0])); i++) {
1112 from = TimeZone::createTimeZone(kData[i].from);
1113 to = TimeZone::createTimeZone(kData[i].to);
1114 if(!from->hasSameRules(*to)) {
1115 errln("different at %i\n", i);
1116 }
46f4442e 1117 if(!quick) {
73c04bcf
A
1118 for(loc = 0; loc < noLoc; loc++) {
1119 const char* locale = uloc_getAvailable(loc);
1120 for(j = 0; j < (int32_t)(sizeof(styles)/sizeof(styles[0])); j++) {
1121 for(k = 0; k < (int32_t)(sizeof(useDst)/sizeof(useDst[0])); k++) {
1122 fromName.remove();
1123 toName.remove();
1124 from->getDisplayName(useDst[k], styles[j],locale, fromName);
1125 to->getDisplayName(useDst[k], styles[j], locale, toName);
1126 if(fromName.compare(toName) != 0) {
1127 errln("Fail: Expected "+toName+" but got " + prettify(fromName)
1128 + " for locale: " + locale + " index: "+ loc
1129 + " to id "+ kData[i].to
1130 + " from id " + kData[i].from);
1131 }
1132 }
1133 }
1134 }
1135 } else {
1136 fromName.remove();
1137 toName.remove();
1138 from->getDisplayName(fromName);
1139 to->getDisplayName(toName);
1140 if(fromName.compare(toName) != 0) {
1141 errln("Fail: Expected "+toName+" but got " + fromName);
1142 }
1143 }
1144 delete from;
1145 delete to;
1146 }
1147}
1148
b75a7d8f
A
1149/**
1150 * Test the basic functionality of the getDisplayName() API.
1151 *
1152 * @bug 4112869
1153 * @bug 4028006
1154 *
1155 * See also API change request A41.
1156 *
1157 * 4/21/98 - make smarter, so the test works if the ext resources
1158 * are present or not.
1159 */
1160void
1161TimeZoneTest::TestDisplayName()
1162{
1163 UErrorCode status = U_ZERO_ERROR;
1164 int32_t i;
1165 TimeZone *zone = TimeZone::createTimeZone("PST");
1166 UnicodeString name;
1167 zone->getDisplayName(Locale::getEnglish(), name);
1168 logln("PST->" + name);
1169 if (name.compare("Pacific Standard Time") != 0)
1170 errln("Fail: Expected \"Pacific Standard Time\" but got " + name);
1171
1172 //*****************************************************************
1173 // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES
1174 // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES
1175 // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES
1176 //*****************************************************************
1177 struct
1178 {
1179 UBool useDst;
1180 TimeZone::EDisplayType style;
1181 const char *expect;
1182 } kData[] = {
1183 {FALSE, TimeZone::SHORT, "PST"},
1184 {TRUE, TimeZone::SHORT, "PDT"},
1185 {FALSE, TimeZone::LONG, "Pacific Standard Time"},
1186 {TRUE, TimeZone::LONG, "Pacific Daylight Time"},
1187
1188 {FALSE, TimeZone::LONG, ""}
1189 };
1190
1191 for (i=0; kData[i].expect[0] != '\0'; i++)
1192 {
1193 name.remove();
1194 name = zone->getDisplayName(kData[i].useDst,
1195 kData[i].style,
1196 Locale::getEnglish(), name);
1197 if (name.compare(kData[i].expect) != 0)
1198 errln("Fail: Expected " + UnicodeString(kData[i].expect) + "; got " + name);
1199 logln("PST [with options]->" + name);
1200 }
374ca955
A
1201 for (i=0; kData[i].expect[0] != '\0'; i++)
1202 {
b75a7d8f
A
1203 name.remove();
1204 name = zone->getDisplayName(kData[i].useDst,
1205 kData[i].style, name);
1206 if (name.compare(kData[i].expect) != 0)
1207 errln("Fail: Expected " + UnicodeString(kData[i].expect) + "; got " + name);
1208 logln("PST [with options]->" + name);
1209 }
1210
1211
1212 // Make sure that we don't display the DST name by constructing a fake
1213 // PST zone that has DST all year long.
1214 SimpleTimeZone *zone2 = new SimpleTimeZone(0, "PST");
1215
1216 zone2->setStartRule(UCAL_JANUARY, 1, 0, 0, status);
1217 zone2->setEndRule(UCAL_DECEMBER, 31, 0, 0, status);
1218
374ca955
A
1219 UnicodeString inDaylight;
1220 if (zone2->inDaylightTime(UDate(0), status)) {
1221 inDaylight = UnicodeString("TRUE");
1222 } else {
1223 inDaylight = UnicodeString("FALSE");
1224 }
b75a7d8f
A
1225 logln(UnicodeString("Modified PST inDaylightTime->") + inDaylight );
1226 if(U_FAILURE(status))
1227 {
1228 errln("Some sort of error..." + UnicodeString(u_errorName(status))); // REVISIT
1229 }
1230 name.remove();
1231 name = zone2->getDisplayName(Locale::getEnglish(),name);
1232 logln("Modified PST->" + name);
1233 if (name.compare("Pacific Standard Time") != 0)
1234 errln("Fail: Expected \"Pacific Standard Time\"");
1235
1236 // Make sure we get the default display format for Locales
1237 // with no display name data.
374ca955 1238 Locale mt_MT("mt_MT");
b75a7d8f 1239 name.remove();
374ca955 1240 name = zone->getDisplayName(mt_MT,name);
b75a7d8f
A
1241 //*****************************************************************
1242 // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES
1243 // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES
1244 // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES
1245 //*****************************************************************
374ca955 1246 logln("PST(mt_MT)->" + name);
b75a7d8f
A
1247
1248 // *** REVISIT SRL how in the world do I check this? looks java specific.
1249 // Now be smart -- check to see if zh resource is even present.
1250 // If not, we expect the en fallback behavior.
374ca955 1251 ResourceBundle enRB(NULL,
b75a7d8f
A
1252 Locale::getEnglish(), status);
1253 if(U_FAILURE(status))
1254 errln("Couldn't get ResourceBundle for en");
1255
374ca955
A
1256 ResourceBundle mtRB(NULL,
1257 mt_MT, status);
b75a7d8f 1258 //if(U_FAILURE(status))
374ca955 1259 // errln("Couldn't get ResourceBundle for mt_MT");
b75a7d8f
A
1260
1261 UBool noZH = U_FAILURE(status);
1262
1263 if (noZH) {
374ca955 1264 logln("Warning: Not testing the mt_MT behavior because resource is absent");
b75a7d8f
A
1265 if (name != "Pacific Standard Time")
1266 errln("Fail: Expected Pacific Standard Time");
1267 }
1268
1269
1270 if (name.compare("GMT-08:00") &&
1271 name.compare("GMT-8:00") &&
1272 name.compare("GMT-0800") &&
1273 name.compare("GMT-800")) {
374ca955 1274 errln(UnicodeString("Fail: Expected GMT-08:00 or something similar for PST in mt_MT but got ") + name );
b75a7d8f
A
1275 errln("************************************************************");
1276 errln("THE ABOVE FAILURE MAY JUST MEAN THE LOCALE DATA HAS CHANGED");
1277 errln("************************************************************");
1278 }
1279
1280 // Now try a non-existent zone
1281 delete zone2;
1282 zone2 = new SimpleTimeZone(90*60*1000, "xyzzy");
1283 name.remove();
1284 name = zone2->getDisplayName(Locale::getEnglish(),name);
1285 logln("GMT+90min->" + name);
1286 if (name.compare("GMT+01:30") &&
1287 name.compare("GMT+1:30") &&
1288 name.compare("GMT+0130") &&
1289 name.compare("GMT+130"))
1290 errln("Fail: Expected GMT+01:30 or something similar");
374ca955 1291 name.truncate(0);
b75a7d8f
A
1292 zone2->getDisplayName(name);
1293 logln("GMT+90min->" + name);
1294 if (name.compare("GMT+01:30") &&
1295 name.compare("GMT+1:30") &&
1296 name.compare("GMT+0130") &&
1297 name.compare("GMT+130"))
1298 errln("Fail: Expected GMT+01:30 or something similar");
1299 // clean up
1300 delete zone;
1301 delete zone2;
1302}
1303
1304/**
1305 * @bug 4107276
1306 */
1307void
1308TimeZoneTest::TestDSTSavings()
1309{
1310 UErrorCode status = U_ZERO_ERROR;
1311 // It might be better to find a way to integrate this test into the main TimeZone
1312 // tests above, but I don't have time to figure out how to do this (or if it's
1313 // even really a good idea). Let's consider that a future. --rtg 1/27/98
1314 SimpleTimeZone *tz = new SimpleTimeZone(-5 * U_MILLIS_PER_HOUR, "dstSavingsTest",
1315 UCAL_MARCH, 1, 0, 0, UCAL_SEPTEMBER, 1, 0, 0,
1316 (int32_t)(0.5 * U_MILLIS_PER_HOUR), status);
1317 if(U_FAILURE(status))
1318 errln("couldn't create TimeZone");
1319
1320 if (tz->getRawOffset() != -5 * U_MILLIS_PER_HOUR)
1321 errln(UnicodeString("Got back a raw offset of ") + (tz->getRawOffset() / U_MILLIS_PER_HOUR) +
1322 " hours instead of -5 hours.");
1323 if (!tz->useDaylightTime())
1324 errln("Test time zone should use DST but claims it doesn't.");
1325 if (tz->getDSTSavings() != 0.5 * U_MILLIS_PER_HOUR)
1326 errln(UnicodeString("Set DST offset to 0.5 hour, but got back ") + (tz->getDSTSavings() /
1327 U_MILLIS_PER_HOUR) + " hours instead.");
1328
1329 int32_t offset = tz->getOffset(GregorianCalendar::AD, 1998, UCAL_JANUARY, 1,
1330 UCAL_THURSDAY, 10 * U_MILLIS_PER_HOUR,status);
1331 if (offset != -5 * U_MILLIS_PER_HOUR)
1332 errln(UnicodeString("The offset for 10 AM, 1/1/98 should have been -5 hours, but we got ")
1333 + (offset / U_MILLIS_PER_HOUR) + " hours.");
1334
1335 offset = tz->getOffset(GregorianCalendar::AD, 1998, UCAL_JUNE, 1, UCAL_MONDAY,
1336 10 * U_MILLIS_PER_HOUR,status);
1337 if (offset != -4.5 * U_MILLIS_PER_HOUR)
1338 errln(UnicodeString("The offset for 10 AM, 6/1/98 should have been -4.5 hours, but we got ")
1339 + (offset / U_MILLIS_PER_HOUR) + " hours.");
1340
1341 tz->setDSTSavings(U_MILLIS_PER_HOUR, status);
1342 offset = tz->getOffset(GregorianCalendar::AD, 1998, UCAL_JANUARY, 1,
1343 UCAL_THURSDAY, 10 * U_MILLIS_PER_HOUR,status);
1344 if (offset != -5 * U_MILLIS_PER_HOUR)
1345 errln(UnicodeString("The offset for 10 AM, 1/1/98 should have been -5 hours, but we got ")
1346 + (offset / U_MILLIS_PER_HOUR) + " hours.");
1347
1348 offset = tz->getOffset(GregorianCalendar::AD, 1998, UCAL_JUNE, 1, UCAL_MONDAY,
1349 10 * U_MILLIS_PER_HOUR,status);
1350 if (offset != -4 * U_MILLIS_PER_HOUR)
1351 errln(UnicodeString("The offset for 10 AM, 6/1/98 (with a 1-hour DST offset) should have been -4 hours, but we got ")
1352 + (offset / U_MILLIS_PER_HOUR) + " hours.");
1353
1354 delete tz;
1355}
1356
1357/**
1358 * @bug 4107570
1359 */
1360void
1361TimeZoneTest::TestAlternateRules()
1362{
1363 // Like TestDSTSavings, this test should probably be integrated somehow with the main
1364 // test at the top of this class, but I didn't have time to figure out how to do that.
1365 // --rtg 1/28/98
1366
1367 SimpleTimeZone tz(-5 * U_MILLIS_PER_HOUR, "alternateRuleTest");
1368
1369 // test the day-of-month API
1370 UErrorCode status = U_ZERO_ERROR;
1371 tz.setStartRule(UCAL_MARCH, 10, 12 * U_MILLIS_PER_HOUR, status);
1372 if(U_FAILURE(status))
1373 errln("tz.setStartRule failed");
1374 tz.setEndRule(UCAL_OCTOBER, 20, 12 * U_MILLIS_PER_HOUR, status);
1375 if(U_FAILURE(status))
1376 errln("tz.setStartRule failed");
1377
1378 int32_t offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_MARCH, 5,
1379 UCAL_THURSDAY, 10 * U_MILLIS_PER_HOUR,status);
1380 if (offset != -5 * U_MILLIS_PER_HOUR)
1381 errln(UnicodeString("The offset for 10AM, 3/5/98 should have been -5 hours, but we got ")
1382 + (offset / U_MILLIS_PER_HOUR) + " hours.");
1383
1384 offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_MARCH, 15,
1385 UCAL_SUNDAY, 10 * millisPerHour,status);
1386 if (offset != -4 * U_MILLIS_PER_HOUR)
1387 errln(UnicodeString("The offset for 10AM, 3/15/98 should have been -4 hours, but we got ")
1388 + (offset / U_MILLIS_PER_HOUR) + " hours.");
1389
1390 offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_OCTOBER, 15,
1391 UCAL_THURSDAY, 10 * millisPerHour,status);
1392 if (offset != -4 * U_MILLIS_PER_HOUR)
1393 errln(UnicodeString("The offset for 10AM, 10/15/98 should have been -4 hours, but we got ") + (offset / U_MILLIS_PER_HOUR) + " hours.");
1394
1395 offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_OCTOBER, 25,
1396 UCAL_SUNDAY, 10 * millisPerHour,status);
1397 if (offset != -5 * U_MILLIS_PER_HOUR)
1398 errln(UnicodeString("The offset for 10AM, 10/25/98 should have been -5 hours, but we got ")
1399 + (offset / U_MILLIS_PER_HOUR) + " hours.");
1400
1401 // test the day-of-week-after-day-in-month API
1402 tz.setStartRule(UCAL_MARCH, 10, UCAL_FRIDAY, 12 * millisPerHour, TRUE, status);
1403 if(U_FAILURE(status))
1404 errln("tz.setStartRule failed");
1405 tz.setEndRule(UCAL_OCTOBER, 20, UCAL_FRIDAY, 12 * millisPerHour, FALSE, status);
1406 if(U_FAILURE(status))
1407 errln("tz.setStartRule failed");
1408
1409 offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_MARCH, 11,
1410 UCAL_WEDNESDAY, 10 * millisPerHour,status);
1411 if (offset != -5 * U_MILLIS_PER_HOUR)
1412 errln(UnicodeString("The offset for 10AM, 3/11/98 should have been -5 hours, but we got ")
1413 + (offset / U_MILLIS_PER_HOUR) + " hours.");
1414
1415 offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_MARCH, 14,
1416 UCAL_SATURDAY, 10 * millisPerHour,status);
1417 if (offset != -4 * U_MILLIS_PER_HOUR)
1418 errln(UnicodeString("The offset for 10AM, 3/14/98 should have been -4 hours, but we got ")
1419 + (offset / U_MILLIS_PER_HOUR) + " hours.");
1420
1421 offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_OCTOBER, 15,
1422 UCAL_THURSDAY, 10 * millisPerHour,status);
1423 if (offset != -4 * U_MILLIS_PER_HOUR)
1424 errln(UnicodeString("The offset for 10AM, 10/15/98 should have been -4 hours, but we got ")
1425 + (offset / U_MILLIS_PER_HOUR) + " hours.");
1426
1427 offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_OCTOBER, 17,
1428 UCAL_SATURDAY, 10 * millisPerHour,status);
1429 if (offset != -5 * U_MILLIS_PER_HOUR)
1430 errln(UnicodeString("The offset for 10AM, 10/17/98 should have been -5 hours, but we got ")
1431 + (offset / U_MILLIS_PER_HOUR) + " hours.");
1432}
1433
73c04bcf
A
1434void TimeZoneTest::TestFractionalDST() {
1435 const char* tzName = "Australia/Lord_Howe"; // 30 min offset
1436 TimeZone* tz_icu = TimeZone::createTimeZone(tzName);
1437 int dst_icu = tz_icu->getDSTSavings();
1438 UnicodeString id;
1439 int32_t expected = 1800000;
1440 if (expected != dst_icu) {
1441 errln(UnicodeString("java reports dst savings of ") + expected +
1442 " but icu reports " + dst_icu +
1443 " for tz " + tz_icu->getID(id));
1444 } else {
1445 logln(UnicodeString("both java and icu report dst savings of ") + expected + " for tz " + tz_icu->getID(id));
1446 }
1447 delete tz_icu;
1448}
1449
b75a7d8f
A
1450/**
1451 * Test country code support. Jitterbug 776.
1452 */
1453void TimeZoneTest::TestCountries() {
1454 // Make sure America/Los_Angeles is in the "US" group, and
1455 // Asia/Tokyo isn't. Vice versa for the "JP" group.
1456 UErrorCode ec = U_ZERO_ERROR;
1457 int32_t n;
1458 StringEnumeration* s = TimeZone::createEnumeration("US");
1459 n = s->count(ec);
1460 UBool la = FALSE, tokyo = FALSE;
1461 UnicodeString laZone("America/Los_Angeles", "");
1462 UnicodeString tokyoZone("Asia/Tokyo", "");
1463 int32_t i;
1464
1465 if (s == NULL || n <= 0) {
1466 errln("FAIL: TimeZone::createEnumeration() returned nothing");
1467 return;
1468 }
1469 for (i=0; i<n; ++i) {
1470 const UnicodeString* id = s->snext(ec);
1471 if (*id == (laZone)) {
1472 la = TRUE;
1473 }
1474 if (*id == (tokyoZone)) {
1475 tokyo = TRUE;
1476 }
1477 }
1478 if (!la || tokyo) {
1479 errln("FAIL: " + laZone + " in US = " + la);
1480 errln("FAIL: " + tokyoZone + " in US = " + tokyo);
1481 }
1482 delete s;
1483
1484 s = TimeZone::createEnumeration("JP");
1485 n = s->count(ec);
1486 la = FALSE; tokyo = FALSE;
1487
1488 for (i=0; i<n; ++i) {
1489 const UnicodeString* id = s->snext(ec);
1490 if (*id == (laZone)) {
1491 la = TRUE;
1492 }
1493 if (*id == (tokyoZone)) {
1494 tokyo = TRUE;
1495 }
1496 }
1497 if (la || !tokyo) {
1498 errln("FAIL: " + laZone + " in JP = " + la);
1499 errln("FAIL: " + tokyoZone + " in JP = " + tokyo);
1500 }
374ca955
A
1501 StringEnumeration* s1 = TimeZone::createEnumeration("US");
1502 StringEnumeration* s2 = TimeZone::createEnumeration("US");
1503 for(i=0;i<n;++i){
1504 const UnicodeString* id1 = s1->snext(ec);
1505 if(id1==NULL || U_FAILURE(ec)){
1506 errln("Failed to fetch next from TimeZone enumeration. Length returned : %i Current Index: %i", n,i);
1507 }
1508 TimeZone* tz1 = TimeZone::createTimeZone(*id1);
1509 for(int j=0; j<n;++j){
1510 const UnicodeString* id2 = s2->snext(ec);
1511 if(id2==NULL || U_FAILURE(ec)){
1512 errln("Failed to fetch next from TimeZone enumeration. Length returned : %i Current Index: %i", n,i);
1513 }
1514 TimeZone* tz2 = TimeZone::createTimeZone(*id2);
1515 if(tz1->hasSameRules(*tz2)){
1516 logln("ID1 : " + *id1+" == ID2 : " +*id2);
1517 }
1518 delete tz2;
1519 }
1520 delete tz1;
1521 }
1522 delete s1;
1523 delete s2;
b75a7d8f
A
1524 delete s;
1525}
1526
374ca955
A
1527void TimeZoneTest::TestHistorical() {
1528 const int32_t H = U_MILLIS_PER_HOUR;
1529 struct {
1530 const char* id;
1531 int32_t time; // epoch seconds
1532 int32_t offset; // total offset (millis)
1533 } DATA[] = {
1534 // Add transition points (before/after) as desired to test historical
1535 // behavior.
1536 {"America/Los_Angeles", 638963999, -8*H}, // Sun Apr 01 01:59:59 GMT-08:00 1990
1537 {"America/Los_Angeles", 638964000, -7*H}, // Sun Apr 01 03:00:00 GMT-07:00 1990
1538 {"America/Los_Angeles", 657104399, -7*H}, // Sun Oct 28 01:59:59 GMT-07:00 1990
1539 {"America/Los_Angeles", 657104400, -8*H}, // Sun Oct 28 01:00:00 GMT-08:00 1990
1540 {"America/Goose_Bay", -116445601, -4*H}, // Sun Apr 24 01:59:59 GMT-04:00 1966
1541 {"America/Goose_Bay", -116445600, -3*H}, // Sun Apr 24 03:00:00 GMT-03:00 1966
1542 {"America/Goose_Bay", -100119601, -3*H}, // Sun Oct 30 01:59:59 GMT-03:00 1966
1543 {"America/Goose_Bay", -100119600, -4*H}, // Sun Oct 30 01:00:00 GMT-04:00 1966
1544 {"America/Goose_Bay", -84391201, -4*H}, // Sun Apr 30 01:59:59 GMT-04:00 1967
1545 {"America/Goose_Bay", -84391200, -3*H}, // Sun Apr 30 03:00:00 GMT-03:00 1967
1546 {"America/Goose_Bay", -68670001, -3*H}, // Sun Oct 29 01:59:59 GMT-03:00 1967
1547 {"America/Goose_Bay", -68670000, -4*H}, // Sun Oct 29 01:00:00 GMT-04:00 1967
1548 {0, 0, 0}
1549 };
1550
1551 for (int32_t i=0; DATA[i].id!=0; ++i) {
1552 const char* id = DATA[i].id;
1553 TimeZone *tz = TimeZone::createTimeZone(id);
1554 UnicodeString s;
1555 if (tz == 0) {
1556 errln("FAIL: Cannot create %s", id);
1557 } else if (tz->getID(s) != UnicodeString(id)) {
1558 errln((UnicodeString)"FAIL: createTimeZone(" + id + ") => " + s);
1559 } else {
1560 UErrorCode ec = U_ZERO_ERROR;
1561 int32_t raw, dst;
1562 UDate when = (double) DATA[i].time * U_MILLIS_PER_SECOND;
1563 tz->getOffset(when, FALSE, raw, dst, ec);
1564 if (U_FAILURE(ec)) {
1565 errln("FAIL: getOffset");
1566 } else if ((raw+dst) != DATA[i].offset) {
1567 errln((UnicodeString)"FAIL: " + DATA[i].id + ".getOffset(" +
1568 //when + " = " +
1569 dateToString(when) + ") => " +
1570 raw + ", " + dst);
1571 } else {
1572 logln((UnicodeString)"Ok: " + DATA[i].id + ".getOffset(" +
1573 //when + " = " +
1574 dateToString(when) + ") => " +
1575 raw + ", " + dst);
1576 }
1577 }
1578 delete tz;
1579 }
1580}
1581
1582void TimeZoneTest::TestEquivalentIDs() {
1583 int32_t n = TimeZone::countEquivalentIDs("PST");
1584 if (n < 2) {
1585 errln((UnicodeString)"FAIL: countEquivalentIDs(PST) = " + n);
1586 } else {
1587 UBool sawLA = FALSE;
1588 for (int32_t i=0; i<n; ++i) {
1589 UnicodeString id = TimeZone::getEquivalentID("PST", i);
1590 logln((UnicodeString)"" + i + " : " + id);
1591 if (id == UnicodeString("America/Los_Angeles")) {
1592 sawLA = TRUE;
1593 }
1594 }
1595 if (!sawLA) {
1596 errln("FAIL: America/Los_Angeles should be in the list");
1597 }
1598 }
1599}
1600
46f4442e
A
1601// Test that a transition at the end of February is handled correctly.
1602void TimeZoneTest::TestFebruary() {
1603 UErrorCode status = U_ZERO_ERROR;
1604
1605 // Time zone with daylight savings time from the first Sunday in November
1606 // to the last Sunday in February.
1607 // Similar to the new rule for Brazil (Sao Paulo) in tzdata2006n.
1608 //
1609 // Note: In tzdata2007h, the rule had changed, so no actual zones uses
1610 // lastSun in Feb anymore.
1611 SimpleTimeZone tz1(-3 * U_MILLIS_PER_HOUR, // raw offset: 3h before (west of) GMT
1612 UNICODE_STRING("nov-feb", 7),
1613 UCAL_NOVEMBER, 1, UCAL_SUNDAY, // start: November, first, Sunday
1614 0, // midnight wall time
1615 UCAL_FEBRUARY, -1, UCAL_SUNDAY, // end: February, last, Sunday
1616 0, // midnight wall time
1617 status);
1618 if (U_FAILURE(status)) {
1619 errln("Unable to create the SimpleTimeZone(nov-feb): %s", u_errorName(status));
1620 return;
1621 }
1622
1623 // Now hardcode the same rules as for Brazil in tzdata 2006n, so that
1624 // we cover the intended code even when in the future zoneinfo hardcodes
1625 // these transition dates.
1626 SimpleTimeZone tz2(-3 * U_MILLIS_PER_HOUR, // raw offset: 3h before (west of) GMT
1627 UNICODE_STRING("nov-feb2", 8),
1628 UCAL_NOVEMBER, 1, -UCAL_SUNDAY, // start: November, 1 or after, Sunday
1629 0, // midnight wall time
1630 UCAL_FEBRUARY, -29, -UCAL_SUNDAY,// end: February, 29 or before, Sunday
1631 0, // midnight wall time
1632 status);
1633 if (U_FAILURE(status)) {
1634 errln("Unable to create the SimpleTimeZone(nov-feb2): %s", u_errorName(status));
1635 return;
1636 }
1637
1638 // Gregorian calendar with the UTC time zone for getting sample test date/times.
1639 GregorianCalendar gc(*TimeZone::getGMT(), status);
1640 if (U_FAILURE(status)) {
1641 errln("Unable to create the UTC calendar: %s", u_errorName(status));
1642 return;
1643 }
1644
1645 struct {
1646 // UTC time.
1647 int32_t year, month, day, hour, minute, second;
1648 // Expected time zone offset in hours after GMT (negative=before GMT).
1649 int32_t offsetHours;
1650 } data[] = {
1651 { 2006, UCAL_NOVEMBER, 5, 02, 59, 59, -3 },
1652 { 2006, UCAL_NOVEMBER, 5, 03, 00, 00, -2 },
1653 { 2007, UCAL_FEBRUARY, 25, 01, 59, 59, -2 },
1654 { 2007, UCAL_FEBRUARY, 25, 02, 00, 00, -3 },
1655
1656 { 2007, UCAL_NOVEMBER, 4, 02, 59, 59, -3 },
1657 { 2007, UCAL_NOVEMBER, 4, 03, 00, 00, -2 },
1658 { 2008, UCAL_FEBRUARY, 24, 01, 59, 59, -2 },
1659 { 2008, UCAL_FEBRUARY, 24, 02, 00, 00, -3 },
1660
1661 { 2008, UCAL_NOVEMBER, 2, 02, 59, 59, -3 },
1662 { 2008, UCAL_NOVEMBER, 2, 03, 00, 00, -2 },
1663 { 2009, UCAL_FEBRUARY, 22, 01, 59, 59, -2 },
1664 { 2009, UCAL_FEBRUARY, 22, 02, 00, 00, -3 },
1665
1666 { 2009, UCAL_NOVEMBER, 1, 02, 59, 59, -3 },
1667 { 2009, UCAL_NOVEMBER, 1, 03, 00, 00, -2 },
1668 { 2010, UCAL_FEBRUARY, 28, 01, 59, 59, -2 },
1669 { 2010, UCAL_FEBRUARY, 28, 02, 00, 00, -3 }
1670 };
1671
1672 TimeZone *timezones[] = { &tz1, &tz2 };
1673
1674 TimeZone *tz;
1675 UDate dt;
1676 int32_t t, i, raw, dst;
1677 for (t = 0; t < LENGTHOF(timezones); ++t) {
1678 tz = timezones[t];
1679 for (i = 0; i < LENGTHOF(data); ++i) {
1680 gc.set(data[i].year, data[i].month, data[i].day,
1681 data[i].hour, data[i].minute, data[i].second);
1682 dt = gc.getTime(status);
1683 if (U_FAILURE(status)) {
1684 errln("test case %d.%d: bad date/time %04d-%02d-%02d %02d:%02d:%02d",
1685 t, i,
1686 data[i].year, data[i].month + 1, data[i].day,
1687 data[i].hour, data[i].minute, data[i].second);
1688 status = U_ZERO_ERROR;
1689 continue;
1690 }
1691 tz->getOffset(dt, FALSE, raw, dst, status);
1692 if (U_FAILURE(status)) {
1693 errln("test case %d.%d: tz.getOffset(%04d-%02d-%02d %02d:%02d:%02d) fails: %s",
1694 t, i,
1695 data[i].year, data[i].month + 1, data[i].day,
1696 data[i].hour, data[i].minute, data[i].second,
1697 u_errorName(status));
1698 status = U_ZERO_ERROR;
1699 } else if ((raw + dst) != data[i].offsetHours * U_MILLIS_PER_HOUR) {
1700 errln("test case %d.%d: tz.getOffset(%04d-%02d-%02d %02d:%02d:%02d) returns %d+%d != %d",
1701 t, i,
1702 data[i].year, data[i].month + 1, data[i].day,
1703 data[i].hour, data[i].minute, data[i].second,
1704 raw, dst, data[i].offsetHours * U_MILLIS_PER_HOUR);
1705 }
1706 }
1707 }
1708}
1709void TimeZoneTest::TestCanonicalID() {
1710
1711 // Some canonical IDs in CLDR are defined as "Link"
1712 // in Olson tzdata.
1713 static const struct {
1714 const char *alias;
1715 const char *zone;
1716 } excluded1[] = {
1717 {"America/Shiprock", "America/Denver"}, // America/Shiprock is defined as a Link to America/Denver in tzdata
1718 {"America/Marigot", "America/Guadeloupe"},
1719 {"America/St_Barthelemy", "America/Guadeloupe"},
1720 {"Antarctica/South_Pole", "Antarctica/McMurdo"},
1721 {"Atlantic/Jan_Mayen", "Europe/Oslo"},
1722 {"Arctic/Longyearbyen", "Europe/Oslo"},
1723 {"Europe/Guernsey", "Europe/London"},
1724 {"Europe/Isle_of_Man", "Europe/London"},
1725 {"Europe/Jersey", "Europe/London"},
1726 {"Europe/Ljubljana", "Europe/Belgrade"},
1727 {"Europe/Podgorica", "Europe/Belgrade"},
1728 {"Europe/Sarajevo", "Europe/Belgrade"},
1729 {"Europe/Skopje", "Europe/Belgrade"},
1730 {"Europe/Zagreb", "Europe/Belgrade"},
1731 {"Europe/Bratislava", "Europe/Prague"},
1732 {"Europe/Mariehamn", "Europe/Helsinki"},
1733 {"Europe/San_Marino", "Europe/Rome"},
1734 {"Europe/Vatican", "Europe/Rome"},
1735 {0, 0}
1736 };
1737
1738 // Following IDs are aliases of Etc/GMT in CLDR,
1739 // but Olson tzdata has 3 independent definitions
1740 // for Etc/GMT, Etc/UTC, Etc/UCT.
1741 // Until we merge them into one equivalent group
1742 // in zoneinfo.res, we exclude them in the test
1743 // below.
1744 static const char* excluded2[] = {
1745 "Etc/UCT", "UCT",
1746 "Etc/UTC", "UTC",
1747 "Etc/Universal", "Universal",
1748 "Etc/Zulu", "Zulu", 0
1749 };
1750
1751 // Walk through equivalency groups
1752 UErrorCode ec = U_ZERO_ERROR;
1753 int32_t s_length, i, j, k;
1754 StringEnumeration* s = TimeZone::createEnumeration();
1755 UnicodeString canonicalID, tmpCanonical;
1756 s_length = s->count(ec);
1757 for (i = 0; i < s_length;++i) {
1758 const UnicodeString *tzid = s->snext(ec);
1759 int32_t nEquiv = TimeZone::countEquivalentIDs(*tzid);
1760 if (nEquiv == 0) {
1761 continue;
1762 }
1763 UBool bFoundCanonical = FALSE;
1764 // Make sure getCanonicalID returns the exact same result
1765 // for all entries within a same equivalency group with some
1766 // exceptions listed in exluded1.
1767 // Also, one of them must be canonical id.
1768 for (j = 0; j < nEquiv; j++) {
1769 UnicodeString tmp = TimeZone::getEquivalentID(*tzid, j);
1770 TimeZone::getCanonicalID(tmp, tmpCanonical, ec);
1771 if (U_FAILURE(ec)) {
1772 errln((UnicodeString)"FAIL: getCanonicalID(" + tmp + ") failed.");
1773 ec = U_ZERO_ERROR;
1774 continue;
1775 }
1776 // Some exceptional cases
1777 for (k = 0; excluded1[k].alias != 0; k++) {
1778 if (tmpCanonical == excluded1[k].alias) {
1779 tmpCanonical = excluded1[k].zone;
1780 break;
1781 }
1782 }
1783 if (j == 0) {
1784 canonicalID = tmpCanonical;
1785 } else if (canonicalID != tmpCanonical) {
1786 errln("FAIL: getCanonicalID(" + tmp + ") returned " + tmpCanonical + " expected:" + canonicalID);
1787 }
1788
1789 if (canonicalID == tmp) {
1790 bFoundCanonical = TRUE;
1791 }
1792 }
1793 // At least one ID in an equvalency group must match the
1794 // canonicalID
1795 if (bFoundCanonical == FALSE) {
1796 // test exclusion because of differences between Olson tzdata and CLDR
1797 UBool isExcluded = FALSE;
1798 for (k = 0; excluded2[k] != 0; k++) {
1799 if (*tzid == UnicodeString(excluded2[k])) {
1800 isExcluded = TRUE;
1801 break;
1802 }
1803 }
1804 if (isExcluded) {
1805 continue;
1806 }
1807 errln((UnicodeString)"FAIL: No timezone ids match the canonical ID " + canonicalID);
1808 }
1809 }
1810 delete s;
1811
1812 // Testing some special cases
1813 static const struct {
1814 const char *id;
1815 const char *expected;
1816 UBool isSystem;
1817 } data[] = {
1818 {"GMT-03", "GMT-0300", FALSE},
1819 {"GMT+4", "GMT+0400", FALSE},
1820 {"GMT-055", "GMT-0055", FALSE},
1821 {"GMT+430", "GMT+0430", FALSE},
1822 {"GMT-12:15", "GMT-1215", FALSE},
1823 {"GMT-091015", "GMT-091015", FALSE},
1824 {"GMT+1:90", 0, FALSE},
1825 {"America/Argentina/Buenos_Aires", "America/Buenos_Aires", TRUE},
1826 {"bogus", 0, FALSE},
1827 {"", 0, FALSE},
1828 {0, 0, FALSE}
1829 };
1830
1831 UBool isSystemID;
1832 for (i = 0; data[i].id != 0; i++) {
1833 TimeZone::getCanonicalID(UnicodeString(data[i].id), canonicalID, isSystemID, ec);
1834 if (U_FAILURE(ec)) {
1835 if (ec != U_ILLEGAL_ARGUMENT_ERROR || data[i].expected != 0) {
1836 errln((UnicodeString)"FAIL: getCanonicalID(\"" + data[i].id
1837 + "\") returned status U_ILLEGAL_ARGUMENT_ERROR");
1838 }
1839 ec = U_ZERO_ERROR;
1840 continue;
1841 }
1842 if (canonicalID != data[i].expected) {
1843 errln((UnicodeString)"FAIL: getCanonicalID(\"" + data[i].id
1844 + "\") returned " + canonicalID + " - expected: " + data[i].expected);
1845 }
1846 if (isSystemID != data[i].isSystem) {
1847 errln((UnicodeString)"FAIL: getCanonicalID(\"" + data[i].id
1848 + "\") set " + isSystemID + " to isSystemID");
1849 }
1850 }
1851}
1852
1853//
1854// Test Display Names, choosing zones and lcoales where there are multiple
1855// meta-zones defined.
1856//
1857static struct {
1858 const char *zoneName;
1859 const char *localeName;
1860 UBool summerTime;
1861 TimeZone::EDisplayType style;
1862 const char *expectedDisplayName; }
1863 zoneDisplayTestData [] = {
1864 // zone id locale summer format expected display name
1865 {"Europe/London", "en", FALSE, TimeZone::SHORT, "GMT"},
1866 {"Europe/London", "en", FALSE, TimeZone::LONG, "Greenwich Mean Time"},
1867 {"Europe/London", "en", TRUE, TimeZone::SHORT, "GMT+01:00" /*"BST"*/},
1868 {"Europe/London", "en", TRUE, TimeZone::LONG, "British Summer Time"},
1869
1870 {"America/Anchorage", "en", FALSE, TimeZone::SHORT, "AKST"},
1871 {"America/Anchorage", "en", FALSE, TimeZone::LONG, "Alaska Standard Time"},
1872 {"America/Anchorage", "en", TRUE, TimeZone::SHORT, "AKDT"},
1873 {"America/Anchorage", "en", TRUE, TimeZone::LONG, "Alaska Daylight Time"},
1874
1875 // Southern Hemisphere, all data from meta:Australia_Western
1876 {"Australia/Perth", "en", FALSE, TimeZone::SHORT, "GMT+08:00"/*"AWST"*/},
1877 {"Australia/Perth", "en", FALSE, TimeZone::LONG, "Australian Western Standard Time"},
1878 {"Australia/Perth", "en", TRUE, TimeZone::SHORT, "GMT+09:00"/*"AWDT"*/},
1879 {"Australia/Perth", "en", TRUE, TimeZone::LONG, "Australian Western Daylight Time"},
1880
1881 {"America/Sao_Paulo", "en", FALSE, TimeZone::SHORT, "GMT-03:00"/*"BRT"*/},
1882 {"America/Sao_Paulo", "en", FALSE, TimeZone::LONG, "Brasilia Time"},
1883 {"America/Sao_Paulo", "en", TRUE, TimeZone::SHORT, "GMT-02:00"/*"BRST"*/},
1884 {"America/Sao_Paulo", "en", TRUE, TimeZone::LONG, "Brasilia Summer Time"},
1885
1886 // No Summer Time, but had it before 1983.
1887 {"Pacific/Honolulu", "en", FALSE, TimeZone::SHORT, "HST"},
1888 {"Pacific/Honolulu", "en", FALSE, TimeZone::LONG, "Hawaii-Aleutian Standard Time"},
1889 {"Pacific/Honolulu", "en", TRUE, TimeZone::SHORT, "HST"},
1890 {"Pacific/Honolulu", "en", TRUE, TimeZone::LONG, "Hawaii-Aleutian Standard Time"},
1891
1892 // Northern, has Summer, not commonly used.
1893 {"Europe/Helsinki", "en", FALSE, TimeZone::SHORT, "GMT+02:00"/*"EET"*/},
1894 {"Europe/Helsinki", "en", FALSE, TimeZone::LONG, "Eastern European Time"},
1895 {"Europe/Helsinki", "en", TRUE, TimeZone::SHORT, "GMT+03:00"/*"EEST"*/},
1896 {"Europe/Helsinki", "en", true, TimeZone::LONG, "Eastern European Summer Time"},
1897 {NULL, NULL, FALSE, TimeZone::SHORT, NULL} // NULL values terminate list
1898 };
1899
1900void TimeZoneTest::TestDisplayNamesMeta() {
1901 UErrorCode status = U_ZERO_ERROR;
1902 GregorianCalendar cal(*TimeZone::getGMT(), status);
1903 if (failure(status, "GregorianCalendar")) return;
1904
1905 UBool isReferenceYear = TRUE;
1906 if (cal.get(UCAL_YEAR, status) != TimeZoneTest::REFERENCE_YEAR) {
1907 isReferenceYear = FALSE;
1908 }
1909
1910 UBool sawAnError = FALSE;
1911 for (int testNum = 0; zoneDisplayTestData[testNum].zoneName != NULL; testNum++) {
1912 Locale locale = Locale::createFromName(zoneDisplayTestData[testNum].localeName);
1913 TimeZone *zone = TimeZone::createTimeZone(zoneDisplayTestData[testNum].zoneName);
1914 UnicodeString displayName;
1915 zone->getDisplayName(zoneDisplayTestData[testNum].summerTime,
1916 zoneDisplayTestData[testNum].style,
1917 locale,
1918 displayName);
1919 if (displayName != zoneDisplayTestData[testNum].expectedDisplayName) {
1920 char name[100];
1921 UErrorCode status = U_ZERO_ERROR;
1922 displayName.extract(name, 100, NULL, status);
1923 if (isReferenceYear) {
1924 sawAnError = TRUE;
1925 errln("Incorrect time zone display name. zone = \"%s\",\n"
1926 " locale = \"%s\", style = %s, Summertime = %d\n"
1927 " Expected \"%s\", "
1928 " Got \"%s\"\n", zoneDisplayTestData[testNum].zoneName,
1929 zoneDisplayTestData[testNum].localeName,
1930 zoneDisplayTestData[testNum].style==TimeZone::SHORT ?
1931 "SHORT" : "LONG",
1932 zoneDisplayTestData[testNum].summerTime,
1933 zoneDisplayTestData[testNum].expectedDisplayName,
1934 name);
1935 } else {
1936 logln("Incorrect time zone display name. zone = \"%s\",\n"
1937 " locale = \"%s\", style = %s, Summertime = %d\n"
1938 " Expected \"%s\", "
1939 " Got \"%s\"\n", zoneDisplayTestData[testNum].zoneName,
1940 zoneDisplayTestData[testNum].localeName,
1941 zoneDisplayTestData[testNum].style==TimeZone::SHORT ?
1942 "SHORT" : "LONG",
1943 zoneDisplayTestData[testNum].summerTime,
1944 zoneDisplayTestData[testNum].expectedDisplayName,
1945 name);
1946 }
1947 }
1948 delete zone;
1949 }
1950 if (sawAnError) {
1951 errln("Note: Errors could be the result of changes to zoneStrings locale data");
1952 }
1953}
1954
b75a7d8f 1955#endif /* #if !UCONFIG_NO_FORMATTING */