1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 2007-2015, International Business Machines Corporation and *
6 * others. All Rights Reserved. *
7 *******************************************************************************
9 #include "unicode/utypes.h"
11 #if !UCONFIG_NO_FORMATTING
15 #include "unicode/timezone.h"
16 #include "unicode/simpletz.h"
17 #include "unicode/calendar.h"
18 #include "unicode/strenum.h"
19 #include "unicode/smpdtfmt.h"
20 #include "unicode/uchar.h"
21 #include "unicode/basictz.h"
22 #include "unicode/tzfmt.h"
23 #include "unicode/localpointer.h"
24 #include "unicode/utf16.h"
29 #include "simplethread.h"
33 static const char* PATTERNS
[] = {
36 "Z", // equivalent to "xxxx"
37 "ZZZZ", // equivalent to "OOOO"
58 static const UChar ETC_UNKNOWN
[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x6E, 0x6B, 0x6E, 0x6F, 0x77, 0x6E, 0};
60 static const UChar ETC_SLASH
[] = { 0x45, 0x74, 0x63, 0x2F, 0 }; // "Etc/"
61 static const UChar SYSTEMV_SLASH
[] = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F, 0 }; // "SystemV/
62 static const UChar RIYADH8
[] = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38, 0 }; // "Riyadh8"
64 static UBool
contains(const char** list
, const char* str
) {
65 for (int32_t i
= 0; list
[i
]; i
++) {
66 if (uprv_strcmp(list
[i
], str
) == 0) {
74 TimeZoneFormatTest::runIndexedTest( int32_t index
, UBool exec
, const char* &name
, char* /*par*/ )
77 logln("TestSuite TimeZoneFormatTest");
80 TESTCASE(0, TestTimeZoneRoundTrip
);
81 TESTCASE(1, TestTimeRoundTrip
);
82 TESTCASE(2, TestParse
);
83 TESTCASE(3, TestISOFormat
);
84 TESTCASE(4, TestFormat
);
85 TESTCASE(5, TestFormatTZDBNames
);
86 TESTCASE(6, TestFormatCustomZone
);
87 TESTCASE(7, TestFormatTZDBNamesAllZoneCoverage
);
88 default: name
= ""; break;
93 TimeZoneFormatTest::TestTimeZoneRoundTrip(void) {
94 UErrorCode status
= U_ZERO_ERROR
;
96 SimpleTimeZone
unknownZone(-31415, ETC_UNKNOWN
);
97 int32_t badDstOffset
= -1234;
98 int32_t badZoneOffset
= -2345;
100 int32_t testDateData
[][3] = {
109 Calendar
*cal
= Calendar::createInstance(TimeZone::createTimeZone((UnicodeString
)"UTC"), status
);
110 if (U_FAILURE(status
)) {
111 dataerrln("Calendar::createInstance failed: %s", u_errorName(status
));
115 // Set up rule equivalency test range
117 cal
->set(1900, UCAL_JANUARY
, 1);
118 low
= cal
->getTime(status
);
119 cal
->set(2040, UCAL_JANUARY
, 1);
120 high
= cal
->getTime(status
);
121 if (U_FAILURE(status
)) {
122 errln("getTime failed");
127 UDate DATES
[UPRV_LENGTHOF(testDateData
)];
128 const int32_t nDates
= UPRV_LENGTHOF(testDateData
);
130 for (int32_t i
= 0; i
< nDates
; i
++) {
131 cal
->set(testDateData
[i
][0], testDateData
[i
][1], testDateData
[i
][2]);
132 DATES
[i
] = cal
->getTime(status
);
133 if (U_FAILURE(status
)) {
134 errln("getTime failed");
139 // Set up test locales
140 const Locale testLocales
[] = {
149 const Locale
*LOCALES
;
153 LOCALES
= testLocales
;
154 nLocales
= UPRV_LENGTHOF(testLocales
);
156 LOCALES
= Locale::getAvailableLocales(nLocales
);
159 StringEnumeration
*tzids
= TimeZone::createEnumeration();
160 int32_t inRaw
, inDst
;
161 int32_t outRaw
, outDst
;
163 // Run the roundtrip test
164 for (int32_t locidx
= 0; locidx
< nLocales
; locidx
++) {
165 UnicodeString localGMTString
;
166 SimpleDateFormat
gmtFmt(UnicodeString("ZZZZ"), LOCALES
[locidx
], status
);
167 if (U_FAILURE(status
)) {
168 dataerrln("Error creating SimpleDateFormat - %s", u_errorName(status
));
171 gmtFmt
.setTimeZone(*TimeZone::getGMT());
172 gmtFmt
.format(0.0, localGMTString
);
174 for (int32_t patidx
= 0; patidx
< UPRV_LENGTHOF(PATTERNS
); patidx
++) {
175 SimpleDateFormat
*sdf
= new SimpleDateFormat((UnicodeString
)PATTERNS
[patidx
], LOCALES
[locidx
], status
);
176 if (U_FAILURE(status
)) {
177 dataerrln((UnicodeString
)"new SimpleDateFormat failed for pattern " +
178 PATTERNS
[patidx
] + " for locale " + LOCALES
[locidx
].getName() + " - " + u_errorName(status
));
179 status
= U_ZERO_ERROR
;
183 tzids
->reset(status
);
184 const UnicodeString
*tzid
;
185 while ((tzid
= tzids
->snext(status
))) {
186 TimeZone
*tz
= TimeZone::createTimeZone(*tzid
);
188 for (int32_t datidx
= 0; datidx
< nDates
; datidx
++) {
190 FieldPosition
fpos(FieldPosition::DONT_CARE
);
192 sdf
->setTimeZone(*tz
);
193 sdf
->format(DATES
[datidx
], tzstr
, fpos
);
195 // Before parse, set unknown zone to SimpleDateFormat instance
196 // just for making sure that it does not depends on the time zone
198 sdf
->setTimeZone(unknownZone
);
201 ParsePosition
pos(0);
202 Calendar
*outcal
= Calendar::createInstance(unknownZone
, status
);
203 if (U_FAILURE(status
)) {
204 errln("Failed to create an instance of calendar for receiving parse result.");
205 status
= U_ZERO_ERROR
;
208 outcal
->set(UCAL_DST_OFFSET
, badDstOffset
);
209 outcal
->set(UCAL_ZONE_OFFSET
, badZoneOffset
);
211 sdf
->parse(tzstr
, *outcal
, pos
);
214 const TimeZone
&outtz
= outcal
->getTimeZone();
215 UnicodeString outtzid
;
216 outtz
.getID(outtzid
);
218 tz
->getOffset(DATES
[datidx
], false, inRaw
, inDst
, status
);
219 if (U_FAILURE(status
)) {
220 errln((UnicodeString
)"Failed to get offsets from time zone" + *tzid
);
221 status
= U_ZERO_ERROR
;
223 outtz
.getOffset(DATES
[datidx
], false, outRaw
, outDst
, status
);
224 if (U_FAILURE(status
)) {
225 errln((UnicodeString
)"Failed to get offsets from time zone" + outtzid
);
226 status
= U_ZERO_ERROR
;
229 if (uprv_strcmp(PATTERNS
[patidx
], "V") == 0) {
230 // Short zone ID - should support roundtrip for canonical CLDR IDs
231 UnicodeString canonicalID
;
232 TimeZone::getCanonicalID(*tzid
, canonicalID
, status
);
233 if (U_FAILURE(status
)) {
234 // Uknown ID - we should not get here
235 errln((UnicodeString
)"Unknown ID " + *tzid
);
236 status
= U_ZERO_ERROR
;
237 } else if (outtzid
!= canonicalID
) {
238 if (outtzid
.compare(ETC_UNKNOWN
, -1) == 0) {
239 // Note that some zones like Asia/Riyadh87 does not have
240 // short zone ID and "unk" is used as fallback
241 logln((UnicodeString
)"Canonical round trip failed (probably as expected); tz=" + *tzid
242 + ", locale=" + LOCALES
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
243 + ", time=" + DATES
[datidx
] + ", str=" + tzstr
244 + ", outtz=" + outtzid
);
246 errln((UnicodeString
)"Canonical round trip failed; tz=" + *tzid
247 + ", locale=" + LOCALES
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
248 + ", time=" + DATES
[datidx
] + ", str=" + tzstr
249 + ", outtz=" + outtzid
);
252 } else if (uprv_strcmp(PATTERNS
[patidx
], "VV") == 0) {
253 // Zone ID - full roundtrip support
254 if (outtzid
!= *tzid
) {
255 errln((UnicodeString
)"Zone ID round trip failued; tz=" + *tzid
256 + ", locale=" + LOCALES
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
257 + ", time=" + DATES
[datidx
] + ", str=" + tzstr
258 + ", outtz=" + outtzid
);
260 } else if (uprv_strcmp(PATTERNS
[patidx
], "VVV") == 0 || uprv_strcmp(PATTERNS
[patidx
], "VVVV") == 0) {
261 // Location: time zone rule must be preserved except
262 // zones not actually associated with a specific location.
263 // Time zones in this category do not have "/" in its ID.
264 UnicodeString canonical
;
265 TimeZone::getCanonicalID(*tzid
, canonical
, status
);
266 if (U_FAILURE(status
)) {
267 // Uknown ID - we should not get here
268 errln((UnicodeString
)"Unknown ID " + *tzid
);
269 status
= U_ZERO_ERROR
;
270 } else if (outtzid
!= canonical
) {
271 // Canonical ID did not match - check the rules
272 if (!((BasicTimeZone
*)&outtz
)->hasEquivalentTransitions((BasicTimeZone
&)*tz
, low
, high
, TRUE
, status
)) {
273 if (canonical
.indexOf((UChar
)0x27 /*'/'*/) == -1) {
274 // Exceptional cases, such as CET, EET, MET and WET
275 logln((UnicodeString
)"Canonical round trip failed (as expected); tz=" + *tzid
276 + ", locale=" + LOCALES
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
277 + ", time=" + DATES
[datidx
] + ", str=" + tzstr
278 + ", outtz=" + outtzid
);
280 errln((UnicodeString
)"Canonical round trip failed; tz=" + *tzid
281 + ", locale=" + LOCALES
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
282 + ", time=" + DATES
[datidx
] + ", str=" + tzstr
283 + ", outtz=" + outtzid
);
285 if (U_FAILURE(status
)) {
286 errln("hasEquivalentTransitions failed");
287 status
= U_ZERO_ERROR
;
293 UBool isOffsetFormat
= (*PATTERNS
[patidx
] == 'Z'
294 || *PATTERNS
[patidx
] == 'O'
295 || *PATTERNS
[patidx
] == 'X'
296 || *PATTERNS
[patidx
] == 'x');
297 UBool minutesOffset
= FALSE
;
298 if (*PATTERNS
[patidx
] == 'X' || *PATTERNS
[patidx
] == 'x') {
299 minutesOffset
= (uprv_strlen(PATTERNS
[patidx
]) <= 3);
302 if (!isOffsetFormat
) {
303 // Check if localized GMT format is used as a fallback of name styles
304 int32_t numDigits
= 0;
306 while (idx
< tzstr
.length()) {
307 UChar32 cp
= tzstr
.char32At(idx
);
311 idx
+= U16_LENGTH(cp
);
313 isOffsetFormat
= (numDigits
> 0);
315 if (isOffsetFormat
|| tzstr
== localGMTString
) {
316 // Localized GMT or ISO: total offset (raw + dst) must be preserved.
317 int32_t inOffset
= inRaw
+ inDst
;
318 int32_t outOffset
= outRaw
+ outDst
;
319 int32_t diff
= outOffset
- inOffset
;
321 diff
= (diff
/ 60000) * 60000;
324 errln((UnicodeString
)"Offset round trip failed; tz=" + *tzid
325 + ", locale=" + LOCALES
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
326 + ", time=" + DATES
[datidx
] + ", str=" + tzstr
327 + ", inOffset=" + inOffset
+ ", outOffset=" + outOffset
);
330 // Specific or generic: raw offset must be preserved.
331 if (inRaw
!= outRaw
) {
332 errln((UnicodeString
)"Raw offset round trip failed; tz=" + *tzid
333 + ", locale=" + LOCALES
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
334 + ", time=" + DATES
[datidx
] + ", str=" + tzstr
335 + ", inRawOffset=" + inRaw
+ ", outRawOffset=" + outRaw
);
350 // Special exclusions in TestTimeZoneRoundTrip.
351 // These special cases do not round trip time as designed.
352 static UBool
isSpecialTimeRoundTripCase(const char* loc
,
353 const UnicodeString
& id
,
362 {NULL
, "Asia/Chita", "zzzz", 1414252800000.0},
363 {NULL
, "Asia/Chita", "vvvv", 1414252800000.0},
364 {NULL
, "Asia/Srednekolymsk", "zzzz", 1414241999999.0},
365 {NULL
, "Asia/Srednekolymsk", "vvvv", 1414241999999.0},
366 {NULL
, NULL
, NULL
, U_DATE_MIN
}
369 UBool isExcluded
= FALSE
;
370 for (int32_t i
= 0; EXCLUSIONS
[i
].id
!= NULL
; i
++) {
371 if (EXCLUSIONS
[i
].loc
== NULL
|| uprv_strcmp(loc
, EXCLUSIONS
[i
].loc
) == 0) {
372 if (id
.compare(EXCLUSIONS
[i
].id
) == 0) {
373 if (EXCLUSIONS
[i
].pattern
== NULL
|| uprv_strcmp(pattern
, EXCLUSIONS
[i
].pattern
) == 0) {
374 if (EXCLUSIONS
[i
].time
== U_DATE_MIN
|| EXCLUSIONS
[i
].time
== time
) {
384 // LocaleData. Somewhat misnamed. For TestTimeZoneRoundTrip, specifies the locales and patterns
385 // to be tested, and provides an iterator over these for the multi-threaded test
386 // functions to pick up the next combination to be tested.
388 // A single global instance of this struct is shared among all
391 // "locales" is an array of locales to be tested.
392 // PATTERNS (a global) is an array of patterns to be tested for each locale.
393 // "localeIndex" and "patternIndex" keep track of the iteration through the above.
394 // Each of the parallel test threads calls LocaleData::nextTest() in a loop
395 // to find out what to test next. It must be thread safe.
398 int32_t patternIndex
;
400 UDate times
[UPRV_LENGTHOF(PATTERNS
)]; // Performance data, Elapsed time for each pattern.
401 const Locale
* locales
;
407 LocaleData() : localeIndex(0), patternIndex(0), testCounts(0), locales(NULL
),
408 nLocales(0), START_TIME(0), END_TIME(0), numDone(0) {
409 for (int i
=0; i
<UPRV_LENGTHOF(times
); i
++) {
414 void resetTestIteration() {
416 patternIndex
= UPRV_LENGTHOF(PATTERNS
);
420 UBool
nextTest(int32_t &rLocaleIndex
, int32_t &rPatternIndex
) {
422 if (patternIndex
>= UPRV_LENGTHOF(PATTERNS
) - 1) {
423 if (localeIndex
>= nLocales
- 1) {
430 rLocaleIndex
= localeIndex
;
431 rPatternIndex
= patternIndex
;
436 void addTime(UDate amount
, int32_t patIdx
) {
438 U_ASSERT(patIdx
< UPRV_LENGTHOF(PATTERNS
));
439 times
[patIdx
] += amount
;
443 static LocaleData
*gLocaleData
= NULL
;
446 TimeZoneFormatTest::TestTimeRoundTrip(void) {
447 UErrorCode status
= U_ZERO_ERROR
;
448 LocalPointer
<Calendar
> cal(Calendar::createInstance(TimeZone::createTimeZone((UnicodeString
) "UTC"), status
));
449 if (U_FAILURE(status
)) {
450 dataerrln("Calendar::createInstance failed: %s", u_errorName(status
));
454 const char* testAllProp
= getProperty("TimeZoneRoundTripAll");
455 UBool bTestAll
= (testAllProp
&& uprv_strcmp(testAllProp
, "true") == 0);
457 UDate START_TIME
, END_TIME
;
458 if (bTestAll
|| !quick
) {
459 cal
->set(1900, UCAL_JANUARY
, 1);
461 cal
->set(1999, UCAL_JANUARY
, 1);
463 START_TIME
= cal
->getTime(status
);
465 cal
->set(2022, UCAL_JANUARY
, 1);
466 END_TIME
= cal
->getTime(status
);
468 if (U_FAILURE(status
)) {
469 errln("getTime failed");
473 LocaleData localeData
;
474 gLocaleData
= &localeData
;
476 // Set up test locales
477 const Locale locales1
[] = {Locale("en")};
478 const Locale locales2
[] = {
479 Locale("ar_EG"), Locale("bg_BG"), Locale("ca_ES"), Locale("da_DK"), Locale("de"),
480 Locale("de_DE"), Locale("el_GR"), Locale("en"), Locale("en_AU"), Locale("en_CA"),
481 Locale("en_US"), Locale("es"), Locale("es_ES"), Locale("es_MX"), Locale("fi_FI"),
482 Locale("fr"), Locale("fr_CA"), Locale("fr_FR"), Locale("he_IL"), Locale("hu_HU"),
483 Locale("it"), Locale("it_IT"), Locale("ja"), Locale("ja_JP"), Locale("ko"),
484 Locale("ko_KR"), Locale("nb_NO"), Locale("nl_NL"), Locale("nn_NO"), Locale("pl_PL"),
485 Locale("pt"), Locale("pt_BR"), Locale("pt_PT"), Locale("ru_RU"), Locale("sv_SE"),
486 Locale("th_TH"), Locale("tr_TR"), Locale("zh"), Locale("zh_Hans"), Locale("zh_Hans_CN"),
487 Locale("zh_Hant"), Locale("zh_Hant_TW"), Locale("fa"), Locale("ccp")
491 gLocaleData
->locales
= Locale::getAvailableLocales(gLocaleData
->nLocales
);
493 gLocaleData
->locales
= locales1
;
494 gLocaleData
->nLocales
= UPRV_LENGTHOF(locales1
);
496 gLocaleData
->locales
= locales2
;
497 gLocaleData
->nLocales
= UPRV_LENGTHOF(locales2
);
500 gLocaleData
->START_TIME
= START_TIME
;
501 gLocaleData
->END_TIME
= END_TIME
;
502 gLocaleData
->resetTestIteration();
504 // start IntlTest.threadCount threads, each running the function RunTimeRoundTripTests().
506 ThreadPool
<TimeZoneFormatTest
> threads(this, threadCount
, &TimeZoneFormatTest::RunTimeRoundTripTests
);
507 threads
.start(); // Start all threads.
508 threads
.join(); // Wait for all threads to finish.
511 logln("### Elapsed time by patterns ###");
512 for (int32_t i
= 0; i
< UPRV_LENGTHOF(PATTERNS
); i
++) {
513 logln(UnicodeString("") + gLocaleData
->times
[i
] + "ms (" + PATTERNS
[i
] + ")");
514 total
+= gLocaleData
->times
[i
];
516 logln((UnicodeString
) "Total: " + total
+ "ms");
517 logln((UnicodeString
) "Iteration: " + gLocaleData
->testCounts
);
521 // TimeZoneFormatTest::RunTimeRoundTripTests()
522 // This function loops, running time zone format round trip test cases until there are no more, then returns.
523 // Threading: multiple invocations of this function are started in parallel
524 // by TimeZoneFormatTest::TestTimeRoundTrip()
526 void TimeZoneFormatTest::RunTimeRoundTripTests(int32_t threadNumber
) {
527 UErrorCode status
= U_ZERO_ERROR
;
528 UBool REALLY_VERBOSE
= FALSE
;
530 // These patterns are ambiguous at DST->STD local time overlap
531 const char* AMBIGUOUS_DST_DECESSION
[] = { "v", "vvvv", "V", "VV", "VVV", "VVVV", 0 };
533 // These patterns are ambiguous at STD->STD/DST->DST local time overlap
534 const char* AMBIGUOUS_NEGATIVE_SHIFT
[] = { "z", "zzzz", "v", "vvvv", "V", "VV", "VVV", "VVVV", 0 };
536 // These patterns only support integer minutes offset
537 const char* MINUTES_OFFSET
[] = { "X", "XX", "XXX", "x", "xx", "xxx", 0 };
539 // Workaround for #6338
540 //UnicodeString BASEPATTERN("yyyy-MM-dd'T'HH:mm:ss.SSS");
541 UnicodeString
BASEPATTERN("yyyy.MM.dd HH:mm:ss.SSS");
543 // timer for performance analysis
546 UBool expectedRoundTrip
[4];
549 StringEnumeration
*tzids
= TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL
, NULL
, NULL
, status
);
550 if (U_FAILURE(status
)) {
551 if (status
== U_MISSING_RESOURCE_ERROR
) {
552 // This error is generally caused by data not being present.
553 dataerrln("TimeZone::createTimeZoneIDEnumeration failed - %s", u_errorName(status
));
555 errln("TimeZone::createTimeZoneIDEnumeration failed: %s", u_errorName(status
));
563 while (gLocaleData
->nextTest(locidx
, patidx
)) {
565 UnicodeString
pattern(BASEPATTERN
);
566 pattern
.append(" ").append(PATTERNS
[patidx
]);
567 logln(" Thread %d, Locale %s, Pattern %s",
568 threadNumber
, gLocaleData
->locales
[locidx
].getName(), CStr(pattern
)());
570 SimpleDateFormat
*sdf
= new SimpleDateFormat(pattern
, gLocaleData
->locales
[locidx
], status
);
571 if (U_FAILURE(status
)) {
572 errcheckln(status
, (UnicodeString
) "new SimpleDateFormat failed for pattern " +
573 pattern
+ " for locale " + gLocaleData
->locales
[locidx
].getName() + " - " + u_errorName(status
));
574 status
= U_ZERO_ERROR
;
578 UBool minutesOffset
= contains(MINUTES_OFFSET
, PATTERNS
[patidx
]);
580 tzids
->reset(status
);
581 const UnicodeString
*tzid
;
583 timer
= Calendar::getNow();
585 while ((tzid
= tzids
->snext(status
))) {
586 if (uprv_strcmp(PATTERNS
[patidx
], "V") == 0) {
587 // Some zones do not have short ID assigned, such as Asia/Riyadh87.
588 // The time roundtrip will fail for such zones with pattern "V" (short zone ID).
589 // This is expected behavior.
590 const UChar
* shortZoneID
= ZoneMeta::getShortID(*tzid
);
591 if (shortZoneID
== NULL
) {
594 } else if (uprv_strcmp(PATTERNS
[patidx
], "VVV") == 0) {
595 // Some zones are not associated with any region, such as Etc/GMT+8.
596 // The time roundtrip will fail for such zone with pattern "VVV" (exemplar location).
597 // This is expected behavior.
598 if (tzid
->indexOf((UChar
)0x2F) < 0 || tzid
->indexOf(ETC_SLASH
, -1, 0) >= 0
599 || tzid
->indexOf(SYSTEMV_SLASH
, -1, 0) >= 0 || tzid
->indexOf(RIYADH8
, -1, 0) >= 0) {
604 if ((*tzid
== "Pacific/Apia" || *tzid
== "Pacific/Midway" || *tzid
== "Pacific/Pago_Pago")
605 && uprv_strcmp(PATTERNS
[patidx
], "vvvv") == 0
606 && logKnownIssue("11052", "Ambiguous zone name - Samoa Time")) {
610 BasicTimeZone
*tz
= (BasicTimeZone
*) TimeZone::createTimeZone(*tzid
);
611 sdf
->setTimeZone(*tz
);
613 UDate t
= gLocaleData
->START_TIME
;
614 TimeZoneTransition tzt
;
615 UBool tztAvail
= FALSE
;
618 while (t
< gLocaleData
->END_TIME
) {
621 expectedRoundTrip
[0] = TRUE
;
624 int32_t fromOffset
= tzt
.getFrom()->getRawOffset() + tzt
.getFrom()->getDSTSavings();
625 int32_t toOffset
= tzt
.getTo()->getRawOffset() + tzt
.getTo()->getDSTSavings();
626 int32_t delta
= toOffset
- fromOffset
;
628 UBool isDstDecession
= tzt
.getFrom()->getDSTSavings() > 0 && tzt
.getTo()->getDSTSavings() == 0;
629 testTimes
[0] = t
+ delta
- 1;
630 expectedRoundTrip
[0] = TRUE
;
631 testTimes
[1] = t
+ delta
;
632 expectedRoundTrip
[1] = isDstDecession
?
633 !contains(AMBIGUOUS_DST_DECESSION
, PATTERNS
[patidx
]) :
634 !contains(AMBIGUOUS_NEGATIVE_SHIFT
, PATTERNS
[patidx
]);
635 testTimes
[2] = t
- 1;
636 expectedRoundTrip
[2] = isDstDecession
?
637 !contains(AMBIGUOUS_DST_DECESSION
, PATTERNS
[patidx
]) :
638 !contains(AMBIGUOUS_NEGATIVE_SHIFT
, PATTERNS
[patidx
]);
640 expectedRoundTrip
[3] = TRUE
;
643 testTimes
[0] = t
- 1;
644 expectedRoundTrip
[0] = TRUE
;
646 expectedRoundTrip
[1] = TRUE
;
650 for (int32_t testidx
= 0; testidx
< testLen
; testidx
++) {
652 // reduce regular test time
653 if (!expectedRoundTrip
[testidx
]) {
660 gLocaleData
->testCounts
++;
664 FieldPosition
fpos(FieldPosition::DONT_CARE
);
665 sdf
->format(testTimes
[testidx
], text
, fpos
);
667 UDate parsedDate
= sdf
->parse(text
, status
);
668 if (U_FAILURE(status
)) {
669 errln((UnicodeString
) "Parse failure for text=" + text
+ ", tzid=" + *tzid
+ ", locale=" + gLocaleData
->locales
[locidx
].getName()
670 + ", pattern=" + PATTERNS
[patidx
] + ", time=" + testTimes
[testidx
]);
671 status
= U_ZERO_ERROR
;
675 int32_t timeDiff
= (int32_t)(parsedDate
- testTimes
[testidx
]);
676 UBool bTimeMatch
= minutesOffset
?
677 (timeDiff
/60000)*60000 == 0 : timeDiff
== 0;
679 UnicodeString msg
= (UnicodeString
) "Time round trip failed for " + "tzid=" + *tzid
680 + ", locale=" + gLocaleData
->locales
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
681 + ", text=" + text
+ ", time=" + testTimes
[testidx
] + ", restime=" + parsedDate
+ ", diff=" + (parsedDate
- testTimes
[testidx
]);
682 // Timebomb for TZData update
683 if (expectedRoundTrip
[testidx
]
684 && !isSpecialTimeRoundTripCase(gLocaleData
->locales
[locidx
].getName(), *tzid
,
685 PATTERNS
[patidx
], testTimes
[testidx
])) {
686 errln((UnicodeString
) "FAIL: " + msg
);
687 } else if (REALLY_VERBOSE
) {
692 tztAvail
= tz
->getNextTransition(t
, FALSE
, tzt
);
697 // Test the date in the middle of two transitions.
698 t
+= (int64_t) ((tzt
.getTime() - t
) / 2);
707 UDate elapsedTime
= Calendar::getNow() - timer
;
708 gLocaleData
->addTime(elapsedTime
, patidx
);
719 UTimeZoneFormatStyle style
;
720 uint32_t parseOptions
;
721 const char* expected
;
723 UTimeZoneFormatTimeType timeType
;
727 TimeZoneFormatTest::TestParse(void) {
728 const ParseTestData DATA
[] = {
729 // text inPos locale style
730 // parseOptions expected outPos timeType
731 {"Z", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL
,
732 UTZFMT_PARSE_OPTION_NONE
, "Etc/GMT", 1, UTZFMT_TIME_TYPE_UNKNOWN
},
734 {"Z", 0, "en_US", UTZFMT_STYLE_SPECIFIC_LONG
,
735 UTZFMT_PARSE_OPTION_NONE
, "Etc/GMT", 1, UTZFMT_TIME_TYPE_UNKNOWN
},
737 {"Zambia time", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL
,
738 UTZFMT_PARSE_OPTION_ALL_STYLES
, "Etc/GMT", 1, UTZFMT_TIME_TYPE_UNKNOWN
},
740 {"Zambia time", 0, "en_US", UTZFMT_STYLE_GENERIC_LOCATION
,
741 UTZFMT_PARSE_OPTION_NONE
, "Africa/Lusaka", 11, UTZFMT_TIME_TYPE_UNKNOWN
},
743 {"Zambia time", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
,
744 UTZFMT_PARSE_OPTION_ALL_STYLES
, "Africa/Lusaka", 11, UTZFMT_TIME_TYPE_UNKNOWN
},
746 {"+00:00", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL
,
747 UTZFMT_PARSE_OPTION_NONE
, "Etc/GMT", 6, UTZFMT_TIME_TYPE_UNKNOWN
},
749 {"-01:30:45", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL
,
750 UTZFMT_PARSE_OPTION_NONE
, "GMT-01:30:45", 9, UTZFMT_TIME_TYPE_UNKNOWN
},
752 {"-7", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
,
753 UTZFMT_PARSE_OPTION_NONE
, "GMT-07:00", 2, UTZFMT_TIME_TYPE_UNKNOWN
},
755 {"-2222", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
,
756 UTZFMT_PARSE_OPTION_NONE
, "GMT-22:22", 5, UTZFMT_TIME_TYPE_UNKNOWN
},
758 {"-3333", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
,
759 UTZFMT_PARSE_OPTION_NONE
, "GMT-03:33", 4, UTZFMT_TIME_TYPE_UNKNOWN
},
761 {"XXX+01:30YYY", 3, "en_US", UTZFMT_STYLE_LOCALIZED_GMT
,
762 UTZFMT_PARSE_OPTION_NONE
, "GMT+01:30", 9, UTZFMT_TIME_TYPE_UNKNOWN
},
764 {"GMT0", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT
,
765 UTZFMT_PARSE_OPTION_NONE
, "Etc/GMT", 3, UTZFMT_TIME_TYPE_UNKNOWN
},
767 {"EST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT
,
768 UTZFMT_PARSE_OPTION_NONE
, "America/New_York", 3, UTZFMT_TIME_TYPE_STANDARD
},
770 {"ESTx", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT
,
771 UTZFMT_PARSE_OPTION_NONE
, "America/New_York", 3, UTZFMT_TIME_TYPE_STANDARD
},
773 {"EDTx", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT
,
774 UTZFMT_PARSE_OPTION_NONE
, "America/New_York", 3, UTZFMT_TIME_TYPE_DAYLIGHT
},
776 {"EST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_LONG
,
777 UTZFMT_PARSE_OPTION_NONE
, NULL
, 0, UTZFMT_TIME_TYPE_UNKNOWN
},
779 {"EST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_LONG
,
780 UTZFMT_PARSE_OPTION_ALL_STYLES
, "America/New_York", 3, UTZFMT_TIME_TYPE_STANDARD
},
782 {"EST", 0, "en_CA", UTZFMT_STYLE_SPECIFIC_SHORT
,
783 UTZFMT_PARSE_OPTION_NONE
, "America/Toronto", 3, UTZFMT_TIME_TYPE_STANDARD
},
785 {"CST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT
,
786 UTZFMT_PARSE_OPTION_NONE
, "America/Chicago", 3, UTZFMT_TIME_TYPE_STANDARD
},
788 {"CST", 0, "en_GB", UTZFMT_STYLE_SPECIFIC_SHORT
,
789 UTZFMT_PARSE_OPTION_NONE
, NULL
, 0, UTZFMT_TIME_TYPE_UNKNOWN
},
791 {"CST", 0, "en_GB", UTZFMT_STYLE_SPECIFIC_SHORT
,
792 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS
, "America/Chicago", 3, UTZFMT_TIME_TYPE_STANDARD
},
794 {"--CST--", 2, "en_GB", UTZFMT_STYLE_SPECIFIC_SHORT
,
795 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS
, "America/Chicago", 5, UTZFMT_TIME_TYPE_STANDARD
},
797 {"CST", 0, "zh_CN", UTZFMT_STYLE_SPECIFIC_SHORT
,
798 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS
, "Asia/Shanghai", 3, UTZFMT_TIME_TYPE_STANDARD
},
800 {"AEST", 0, "en_AU", UTZFMT_STYLE_SPECIFIC_SHORT
,
801 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS
, "Australia/Sydney", 4, UTZFMT_TIME_TYPE_STANDARD
},
803 {"AST", 0, "ar_SA", UTZFMT_STYLE_SPECIFIC_SHORT
,
804 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS
, "Asia/Riyadh", 3, UTZFMT_TIME_TYPE_STANDARD
},
806 {"AQTST", 0, "en", UTZFMT_STYLE_SPECIFIC_LONG
,
807 UTZFMT_PARSE_OPTION_NONE
, NULL
, 0, UTZFMT_TIME_TYPE_UNKNOWN
},
809 {"AQTST", 0, "en", UTZFMT_STYLE_SPECIFIC_LONG
,
810 UTZFMT_PARSE_OPTION_ALL_STYLES
, NULL
, 0, UTZFMT_TIME_TYPE_UNKNOWN
},
812 {"AQTST", 0, "en", UTZFMT_STYLE_SPECIFIC_LONG
,
813 UTZFMT_PARSE_OPTION_ALL_STYLES
| UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS
, "Asia/Aqtobe", 5, UTZFMT_TIME_TYPE_DAYLIGHT
},
815 {NULL
, 0, NULL
, UTZFMT_STYLE_GENERIC_LOCATION
,
816 UTZFMT_PARSE_OPTION_NONE
, NULL
, 0, UTZFMT_TIME_TYPE_UNKNOWN
}
819 for (int32_t i
= 0; DATA
[i
].text
; i
++) {
820 UErrorCode status
= U_ZERO_ERROR
;
821 LocalPointer
<TimeZoneFormat
> tzfmt(TimeZoneFormat::createInstance(Locale(DATA
[i
].locale
), status
));
822 if (U_FAILURE(status
)) {
823 dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status
));
826 UTimeZoneFormatTimeType ttype
= UTZFMT_TIME_TYPE_UNKNOWN
;
827 ParsePosition
pos(DATA
[i
].inPos
);
828 TimeZone
* tz
= tzfmt
->parse(DATA
[i
].style
, DATA
[i
].text
, pos
, DATA
[i
].parseOptions
, &ttype
);
830 UnicodeString errMsg
;
834 if (outID
!= UnicodeString(DATA
[i
].expected
)) {
835 errMsg
= (UnicodeString
)"Time zone ID: " + outID
+ " - expected: " + DATA
[i
].expected
;
836 } else if (pos
.getIndex() != DATA
[i
].outPos
) {
837 errMsg
= (UnicodeString
)"Parsed pos: " + pos
.getIndex() + " - expected: " + DATA
[i
].outPos
;
838 } else if (ttype
!= DATA
[i
].timeType
) {
839 errMsg
= (UnicodeString
)"Time type: " + ttype
+ " - expected: " + DATA
[i
].timeType
;
843 if (DATA
[i
].expected
) {
844 errMsg
= (UnicodeString
)"Parse failure - expected: " + DATA
[i
].expected
;
847 if (errMsg
.length() > 0) {
848 errln((UnicodeString
)"Fail: " + errMsg
+ " [text=" + DATA
[i
].text
+ ", pos=" + DATA
[i
].inPos
+ ", style=" + DATA
[i
].style
+ "]");
854 TimeZoneFormatTest::TestISOFormat(void) {
855 const int32_t OFFSET
[] = {
860 -77777, // -1m 17.777s
864 -37800000, // -10h 30m
865 -37845000, // -10h 30m 45s
869 const char* ISO_STR
[][11] = {
872 "Z", "Z", "Z", "Z", "Z",
873 "+00", "+0000", "+00:00", "+0000", "+00:00",
878 "Z", "Z", "Z", "Z", "Z",
879 "+00", "+0000", "+00:00", "+0000", "+00:00",
884 "Z", "Z", "Z", "-000059", "-00:00:59",
885 "+00", "+0000", "+00:00", "-000059", "-00:00:59",
890 "+0001", "+0001", "+00:01", "+0001", "+00:01",
891 "+0001", "+0001", "+00:01", "+0001", "+00:01",
896 "-0001", "-0001", "-00:01", "-000117", "-00:01:17",
897 "-0001", "-0001", "-00:01", "-000117", "-00:01:17",
902 "+0030", "+0030", "+00:30", "+0030", "+00:30",
903 "+0030", "+0030", "+00:30", "+0030", "+00:30",
908 "-01", "-0100", "-01:00", "-0100", "-01:00",
909 "-01", "-0100", "-01:00", "-0100", "-01:00",
914 "+10", "+1000", "+10:00", "+1000", "+10:00",
915 "+10", "+1000", "+10:00", "+1000", "+10:00",
920 "-1030", "-1030", "-10:30", "-1030", "-10:30",
921 "-1030", "-1030", "-10:30", "-1030", "-10:30",
926 "-1030", "-1030", "-10:30", "-103045", "-10:30:45",
927 "-1030", "-1030", "-10:30", "-103045", "-10:30:45",
938 const char* PATTERN
[] = {
939 "X", "XX", "XXX", "XXXX", "XXXXX",
940 "x", "xx", "xxx", "xxxx", "xxxxx",
941 "Z", // equivalent to "xxxx"
945 const int32_t MIN_OFFSET_UNIT
[] = {
946 60000, 60000, 60000, 1000, 1000,
947 60000, 60000, 60000, 1000, 1000,
952 UErrorCode status
= U_ZERO_ERROR
;
953 LocalPointer
<SimpleDateFormat
> sdf(new SimpleDateFormat(status
), status
);
954 if (U_FAILURE(status
)) {
955 dataerrln("Fail new SimpleDateFormat: %s", u_errorName(status
));
958 UDate d
= Calendar::getNow();
960 for (uint32_t i
= 0; i
< UPRV_LENGTHOF(OFFSET
); i
++) {
961 SimpleTimeZone
* tz
= new SimpleTimeZone(OFFSET
[i
], UnicodeString("Zone Offset:") + OFFSET
[i
] + "ms");
962 sdf
->adoptTimeZone(tz
);
963 for (int32_t j
= 0; PATTERN
[j
] != 0; j
++) {
964 sdf
->applyPattern(UnicodeString(PATTERN
[j
]));
965 UnicodeString result
;
966 sdf
->format(d
, result
);
969 if (result
!= UnicodeString(ISO_STR
[i
][j
])) {
970 errln((UnicodeString
)"FAIL: pattern=" + PATTERN
[j
] + ", offset=" + OFFSET
[i
] + " -> "
971 + result
+ " (expected: " + ISO_STR
[i
][j
] + ")");
974 // Offset out of range
975 // Note: for now, there is no way to propagate the error status through
976 // the SimpleDateFormat::format above.
977 if (result
.length() > 0) {
978 errln((UnicodeString
)"FAIL: Non-Empty result for pattern=" + PATTERN
[j
] + ", offset=" + OFFSET
[i
]
979 + " (expected: empty result)");
986 LocalPointer
<Calendar
> outcal(Calendar::createInstance(status
));
987 if (U_FAILURE(status
)) {
988 dataerrln("Fail new Calendar: %s", u_errorName(status
));
991 for (int32_t i
= 0; ISO_STR
[i
][0] != NULL
; i
++) {
992 for (int32_t j
= 0; PATTERN
[j
] != 0; j
++) {
993 if (ISO_STR
[i
][j
] == 0) {
996 ParsePosition
pos(0);
997 SimpleTimeZone
* bogusTZ
= new SimpleTimeZone(-1, UnicodeString("Zone Offset: -1ms"));
998 outcal
->adoptTimeZone(bogusTZ
);
999 sdf
->applyPattern(PATTERN
[j
]);
1001 sdf
->parse(UnicodeString(ISO_STR
[i
][j
]), *(outcal
.getAlias()), pos
);
1003 if (pos
.getIndex() != (int32_t)uprv_strlen(ISO_STR
[i
][j
])) {
1004 errln((UnicodeString
)"FAIL: Failed to parse the entire input string: " + ISO_STR
[i
][j
]);
1007 const TimeZone
& outtz
= outcal
->getTimeZone();
1008 int32_t outOffset
= outtz
.getRawOffset();
1009 int32_t adjustedOffset
= OFFSET
[i
] / MIN_OFFSET_UNIT
[j
] * MIN_OFFSET_UNIT
[j
];
1010 if (outOffset
!= adjustedOffset
) {
1011 errln((UnicodeString
)"FAIL: Incorrect offset:" + outOffset
+ "ms for input string: " + ISO_STR
[i
][j
]
1012 + " (expected:" + adjustedOffset
+ "ms)");
1023 UTimeZoneFormatStyle style
;
1024 const char* expected
;
1025 UTimeZoneFormatTimeType timeType
;
1029 TimeZoneFormatTest::TestFormat(void) {
1030 UDate dateJan
= 1358208000000.0; // 2013-01-15T00:00:00Z
1031 UDate dateJul
= 1373846400000.0; // 2013-07-15T00:00:00Z
1033 const FormatTestData DATA
[] = {
1036 "America/Los_Angeles",
1038 UTZFMT_STYLE_GENERIC_LOCATION
,
1040 UTZFMT_TIME_TYPE_UNKNOWN
1044 "America/Los_Angeles",
1046 UTZFMT_STYLE_GENERIC_LONG
,
1048 UTZFMT_TIME_TYPE_UNKNOWN
1052 "America/Los_Angeles",
1054 UTZFMT_STYLE_SPECIFIC_LONG
,
1055 "Pacific Standard Time",
1056 UTZFMT_TIME_TYPE_STANDARD
1060 "America/Los_Angeles",
1062 UTZFMT_STYLE_SPECIFIC_LONG
,
1063 "Pacific Daylight Time",
1064 UTZFMT_TIME_TYPE_DAYLIGHT
1068 "America/Los_Angeles",
1070 UTZFMT_STYLE_ZONE_ID
,
1071 "America/Los_Angeles",
1072 UTZFMT_TIME_TYPE_UNKNOWN
1076 "America/Los_Angeles",
1078 UTZFMT_STYLE_ZONE_ID_SHORT
,
1080 UTZFMT_TIME_TYPE_UNKNOWN
1084 "America/Los_Angeles",
1086 UTZFMT_STYLE_EXEMPLAR_LOCATION
,
1088 UTZFMT_TIME_TYPE_UNKNOWN
1095 UTZFMT_STYLE_GENERIC_LONG
,
1096 "\\u65E5\\u672C\\u6A19\\u6E96\\u6642",
1097 UTZFMT_TIME_TYPE_UNKNOWN
1100 {0, 0, 0.0, UTZFMT_STYLE_GENERIC_LOCATION
, 0, UTZFMT_TIME_TYPE_UNKNOWN
}
1103 for (int32_t i
= 0; DATA
[i
].locale
; i
++) {
1104 UErrorCode status
= U_ZERO_ERROR
;
1105 LocalPointer
<TimeZoneFormat
> tzfmt(TimeZoneFormat::createInstance(Locale(DATA
[i
].locale
), status
));
1106 if (U_FAILURE(status
)) {
1107 dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status
));
1111 LocalPointer
<TimeZone
> tz(TimeZone::createTimeZone(DATA
[i
].tzid
));
1113 UTimeZoneFormatTimeType timeType
;
1115 tzfmt
->format(DATA
[i
].style
, *(tz
.getAlias()), DATA
[i
].date
, out
, &timeType
);
1116 UnicodeString
expected(DATA
[i
].expected
, -1, US_INV
);
1117 expected
= expected
.unescape();
1119 assertEquals(UnicodeString("Format result for ") + DATA
[i
].tzid
+ " (Test Case " + i
+ ")", expected
, out
);
1120 if (DATA
[i
].timeType
!= timeType
) {
1121 dataerrln(UnicodeString("Formatted time zone type (Test Case ") + i
+ "), returned="
1122 + timeType
+ ", expected=" + DATA
[i
].timeType
);
1128 TimeZoneFormatTest::TestFormatTZDBNames(void) {
1129 UDate dateJan
= 1358208000000.0; // 2013-01-15T00:00:00Z
1130 UDate dateJul
= 1373846400000.0; // 2013-07-15T00:00:00Z
1132 const FormatTestData DATA
[] = {
1137 UTZFMT_STYLE_SPECIFIC_SHORT
,
1139 UTZFMT_TIME_TYPE_STANDARD
1145 UTZFMT_STYLE_SPECIFIC_SHORT
,
1147 UTZFMT_TIME_TYPE_STANDARD
1153 UTZFMT_STYLE_SPECIFIC_SHORT
,
1155 UTZFMT_TIME_TYPE_STANDARD
1159 "America/Los_Angeles",
1161 UTZFMT_STYLE_SPECIFIC_LONG
,
1162 "GMT-07:00", // No long display names
1163 UTZFMT_TIME_TYPE_DAYLIGHT
1167 "America/Los_Angeles",
1169 UTZFMT_STYLE_SPECIFIC_SHORT
,
1171 UTZFMT_TIME_TYPE_DAYLIGHT
1177 UTZFMT_STYLE_SPECIFIC_SHORT
,
1179 UTZFMT_TIME_TYPE_DAYLIGHT
1185 UTZFMT_STYLE_SPECIFIC_SHORT
,
1187 UTZFMT_TIME_TYPE_STANDARD
1190 {0, 0, 0.0, UTZFMT_STYLE_GENERIC_LOCATION
, 0, UTZFMT_TIME_TYPE_UNKNOWN
}
1193 for (int32_t i
= 0; DATA
[i
].locale
; i
++) {
1194 UErrorCode status
= U_ZERO_ERROR
;
1195 Locale
loc(DATA
[i
].locale
);
1196 LocalPointer
<TimeZoneFormat
> tzfmt(TimeZoneFormat::createInstance(loc
, status
));
1197 if (U_FAILURE(status
)) {
1198 dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status
));
1201 TimeZoneNames
*tzdbNames
= TimeZoneNames::createTZDBInstance(loc
, status
);
1202 if (U_FAILURE(status
)) {
1203 dataerrln("Fail TimeZoneNames::createTZDBInstance: %s", u_errorName(status
));
1206 tzfmt
->adoptTimeZoneNames(tzdbNames
);
1208 LocalPointer
<TimeZone
> tz(TimeZone::createTimeZone(DATA
[i
].tzid
));
1210 UTimeZoneFormatTimeType timeType
;
1212 tzfmt
->format(DATA
[i
].style
, *(tz
.getAlias()), DATA
[i
].date
, out
, &timeType
);
1213 UnicodeString
expected(DATA
[i
].expected
, -1, US_INV
);
1214 expected
= expected
.unescape();
1216 assertEquals(UnicodeString("Format result for ") + DATA
[i
].tzid
+ " (Test Case " + i
+ ")", expected
, out
);
1217 if (DATA
[i
].timeType
!= timeType
) {
1218 dataerrln(UnicodeString("Formatted time zone type (Test Case ") + i
+ "), returned="
1219 + timeType
+ ", expected=" + DATA
[i
].timeType
);
1225 TimeZoneFormatTest::TestFormatCustomZone(void) {
1229 const char* expected
;
1231 { "abc", 3600000, "GMT+01:00" }, // unknown ID
1232 { "$abc", -3600000, "GMT-01:00" }, // unknown, with ASCII variant char '$'
1233 { "\\u00c1\\u00df\\u00c7", 5400000, "GMT+01:30"}, // unknown, with non-ASCII chars
1237 UDate now
= Calendar::getNow();
1239 for (int32_t i
= 0; ; i
++) {
1240 const char *id
= TESTDATA
[i
].id
;
1244 UnicodeString tzid
= UnicodeString(id
, -1, US_INV
).unescape();
1245 SimpleTimeZone
tz(TESTDATA
[i
].offset
, tzid
);
1247 UErrorCode status
= U_ZERO_ERROR
;
1248 LocalPointer
<TimeZoneFormat
> tzfmt(TimeZoneFormat::createInstance(Locale("en"), status
));
1249 if (tzfmt
.isNull()) {
1250 dataerrln("FAIL: TimeZoneFormat::createInstance failed for en");
1253 UnicodeString tzstr
;
1254 UnicodeString expected
= UnicodeString(TESTDATA
[i
].expected
, -1, US_INV
).unescape();
1256 tzfmt
->format(UTZFMT_STYLE_SPECIFIC_LONG
, tz
, now
, tzstr
, NULL
);
1257 assertEquals(UnicodeString("Format result for ") + tzid
, expected
, tzstr
);
1262 TimeZoneFormatTest::TestFormatTZDBNamesAllZoneCoverage(void) {
1263 UErrorCode status
= U_ZERO_ERROR
;
1264 LocalPointer
<StringEnumeration
> tzids(TimeZone::createEnumeration());
1265 if (tzids
.getAlias() == nullptr) {
1266 dataerrln("%s %d tzids is null", __FILE__
, __LINE__
);
1269 const UnicodeString
*tzid
;
1270 LocalPointer
<TimeZoneNames
> tzdbNames(TimeZoneNames::createTZDBInstance(Locale("en"), status
));
1271 UDate now
= Calendar::getNow();
1274 while ((tzid
= tzids
->snext(status
))) {
1275 logln("Zone: " + *tzid
);
1276 LocalPointer
<TimeZone
> tz(TimeZone::createTimeZone(*tzid
));
1277 tzdbNames
->getMetaZoneID(*tzid
, now
, mzId
);
1278 if (mzId
.isBogus()) {
1279 logln((UnicodeString
)"Meta zone: <not available>");
1281 logln((UnicodeString
)"Meta zone: " + mzId
);
1284 // mzID could be bogus here
1285 tzdbNames
->getMetaZoneDisplayName(mzId
, UTZNM_SHORT_STANDARD
, name
);
1286 // name could be bogus here
1287 if (name
.isBogus()) {
1288 logln((UnicodeString
)"Meta zone short standard name: <not available>");
1291 logln((UnicodeString
)"Meta zone short standard name: " + name
);
1294 tzdbNames
->getMetaZoneDisplayName(mzId
, UTZNM_SHORT_DAYLIGHT
, name
);
1295 // name could be bogus here
1296 if (name
.isBogus()) {
1297 logln((UnicodeString
)"Meta zone short daylight name: <not available>");
1300 logln((UnicodeString
)"Meta zone short daylight name: " + name
);
1305 #endif /* #if !UCONFIG_NO_FORMATTING */