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"
28 #include "simplethread.h"
32 static const char* PATTERNS
[] = {
35 "Z", // equivalent to "xxxx"
36 "ZZZZ", // equivalent to "OOOO"
57 static const UChar ETC_UNKNOWN
[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x6E, 0x6B, 0x6E, 0x6F, 0x77, 0x6E, 0};
59 static const UChar ETC_SLASH
[] = { 0x45, 0x74, 0x63, 0x2F, 0 }; // "Etc/"
60 static const UChar SYSTEMV_SLASH
[] = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F, 0 }; // "SystemV/
61 static const UChar RIYADH8
[] = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38, 0 }; // "Riyadh8"
63 static UBool
contains(const char** list
, const char* str
) {
64 for (int32_t i
= 0; list
[i
]; i
++) {
65 if (uprv_strcmp(list
[i
], str
) == 0) {
73 TimeZoneFormatTest::runIndexedTest( int32_t index
, UBool exec
, const char* &name
, char* /*par*/ )
76 logln("TestSuite TimeZoneFormatTest");
79 TESTCASE(0, TestTimeZoneRoundTrip
);
80 TESTCASE(1, TestTimeRoundTrip
);
81 TESTCASE(2, TestParse
);
82 TESTCASE(3, TestISOFormat
);
83 TESTCASE(4, TestFormat
);
84 TESTCASE(5, TestFormatTZDBNames
);
85 TESTCASE(6, TestFormatCustomZone
);
86 default: name
= ""; break;
91 TimeZoneFormatTest::TestTimeZoneRoundTrip(void) {
92 UErrorCode status
= U_ZERO_ERROR
;
94 SimpleTimeZone
unknownZone(-31415, ETC_UNKNOWN
);
95 int32_t badDstOffset
= -1234;
96 int32_t badZoneOffset
= -2345;
98 int32_t testDateData
[][3] = {
107 Calendar
*cal
= Calendar::createInstance(TimeZone::createTimeZone((UnicodeString
)"UTC"), status
);
108 if (U_FAILURE(status
)) {
109 dataerrln("Calendar::createInstance failed: %s", u_errorName(status
));
113 // Set up rule equivalency test range
115 cal
->set(1900, UCAL_JANUARY
, 1);
116 low
= cal
->getTime(status
);
117 cal
->set(2040, UCAL_JANUARY
, 1);
118 high
= cal
->getTime(status
);
119 if (U_FAILURE(status
)) {
120 errln("getTime failed");
125 UDate DATES
[UPRV_LENGTHOF(testDateData
)];
126 const int32_t nDates
= UPRV_LENGTHOF(testDateData
);
128 for (int32_t i
= 0; i
< nDates
; i
++) {
129 cal
->set(testDateData
[i
][0], testDateData
[i
][1], testDateData
[i
][2]);
130 DATES
[i
] = cal
->getTime(status
);
131 if (U_FAILURE(status
)) {
132 errln("getTime failed");
137 // Set up test locales
138 const Locale testLocales
[] = {
145 const Locale
*LOCALES
;
149 LOCALES
= testLocales
;
150 nLocales
= UPRV_LENGTHOF(testLocales
);
152 LOCALES
= Locale::getAvailableLocales(nLocales
);
155 StringEnumeration
*tzids
= TimeZone::createEnumeration();
156 int32_t inRaw
, inDst
;
157 int32_t outRaw
, outDst
;
159 // Run the roundtrip test
160 for (int32_t locidx
= 0; locidx
< nLocales
; locidx
++) {
161 UnicodeString localGMTString
;
162 SimpleDateFormat
gmtFmt(UnicodeString("ZZZZ"), LOCALES
[locidx
], status
);
163 if (U_FAILURE(status
)) {
164 dataerrln("Error creating SimpleDateFormat - %s", u_errorName(status
));
167 gmtFmt
.setTimeZone(*TimeZone::getGMT());
168 gmtFmt
.format(0.0, localGMTString
);
170 for (int32_t patidx
= 0; patidx
< UPRV_LENGTHOF(PATTERNS
); patidx
++) {
172 SimpleDateFormat
*sdf
= new SimpleDateFormat((UnicodeString
)PATTERNS
[patidx
], LOCALES
[locidx
], status
);
173 if (U_FAILURE(status
)) {
174 dataerrln((UnicodeString
)"new SimpleDateFormat failed for pattern " +
175 PATTERNS
[patidx
] + " for locale " + LOCALES
[locidx
].getName() + " - " + u_errorName(status
));
176 status
= U_ZERO_ERROR
;
180 tzids
->reset(status
);
181 const UnicodeString
*tzid
;
182 while ((tzid
= tzids
->snext(status
))) {
183 TimeZone
*tz
= TimeZone::createTimeZone(*tzid
);
185 for (int32_t datidx
= 0; datidx
< nDates
; datidx
++) {
187 FieldPosition
fpos(FieldPosition::DONT_CARE
);
189 sdf
->setTimeZone(*tz
);
190 sdf
->format(DATES
[datidx
], tzstr
, fpos
);
192 // Before parse, set unknown zone to SimpleDateFormat instance
193 // just for making sure that it does not depends on the time zone
195 sdf
->setTimeZone(unknownZone
);
198 ParsePosition
pos(0);
199 Calendar
*outcal
= Calendar::createInstance(unknownZone
, status
);
200 if (U_FAILURE(status
)) {
201 errln("Failed to create an instance of calendar for receiving parse result.");
202 status
= U_ZERO_ERROR
;
205 outcal
->set(UCAL_DST_OFFSET
, badDstOffset
);
206 outcal
->set(UCAL_ZONE_OFFSET
, badZoneOffset
);
208 sdf
->parse(tzstr
, *outcal
, pos
);
211 const TimeZone
&outtz
= outcal
->getTimeZone();
212 UnicodeString outtzid
;
213 outtz
.getID(outtzid
);
215 tz
->getOffset(DATES
[datidx
], false, inRaw
, inDst
, status
);
216 if (U_FAILURE(status
)) {
217 errln((UnicodeString
)"Failed to get offsets from time zone" + *tzid
);
218 status
= U_ZERO_ERROR
;
220 outtz
.getOffset(DATES
[datidx
], false, outRaw
, outDst
, status
);
221 if (U_FAILURE(status
)) {
222 errln((UnicodeString
)"Failed to get offsets from time zone" + outtzid
);
223 status
= U_ZERO_ERROR
;
226 if (uprv_strcmp(PATTERNS
[patidx
], "V") == 0) {
227 // Short zone ID - should support roundtrip for canonical CLDR IDs
228 UnicodeString canonicalID
;
229 TimeZone::getCanonicalID(*tzid
, canonicalID
, status
);
230 if (U_FAILURE(status
)) {
231 // Uknown ID - we should not get here
232 errln((UnicodeString
)"Unknown ID " + *tzid
);
233 status
= U_ZERO_ERROR
;
234 } else if (outtzid
!= canonicalID
) {
235 if (outtzid
.compare(ETC_UNKNOWN
, -1) == 0) {
236 // Note that some zones like Asia/Riyadh87 does not have
237 // short zone ID and "unk" is used as fallback
238 logln((UnicodeString
)"Canonical round trip failed (probably as expected); tz=" + *tzid
239 + ", locale=" + LOCALES
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
240 + ", time=" + DATES
[datidx
] + ", str=" + tzstr
241 + ", outtz=" + outtzid
);
243 errln((UnicodeString
)"Canonical round trip failed; tz=" + *tzid
244 + ", locale=" + LOCALES
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
245 + ", time=" + DATES
[datidx
] + ", str=" + tzstr
246 + ", outtz=" + outtzid
);
249 } else if (uprv_strcmp(PATTERNS
[patidx
], "VV") == 0) {
250 // Zone ID - full roundtrip support
251 if (outtzid
!= *tzid
) {
252 errln((UnicodeString
)"Zone ID round trip failued; tz=" + *tzid
253 + ", locale=" + LOCALES
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
254 + ", time=" + DATES
[datidx
] + ", str=" + tzstr
255 + ", outtz=" + outtzid
);
257 } else if (uprv_strcmp(PATTERNS
[patidx
], "VVV") == 0 || uprv_strcmp(PATTERNS
[patidx
], "VVVV") == 0) {
258 // Location: time zone rule must be preserved except
259 // zones not actually associated with a specific location.
260 // Time zones in this category do not have "/" in its ID.
261 UnicodeString canonical
;
262 TimeZone::getCanonicalID(*tzid
, canonical
, status
);
263 if (U_FAILURE(status
)) {
264 // Uknown ID - we should not get here
265 errln((UnicodeString
)"Unknown ID " + *tzid
);
266 status
= U_ZERO_ERROR
;
267 } else if (outtzid
!= canonical
) {
268 // Canonical ID did not match - check the rules
269 if (!((BasicTimeZone
*)&outtz
)->hasEquivalentTransitions((BasicTimeZone
&)*tz
, low
, high
, TRUE
, status
)) {
270 if (canonical
.indexOf((UChar
)0x27 /*'/'*/) == -1) {
271 // Exceptional cases, such as CET, EET, MET and WET
272 logln((UnicodeString
)"Canonical round trip failed (as expected); tz=" + *tzid
273 + ", locale=" + LOCALES
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
274 + ", time=" + DATES
[datidx
] + ", str=" + tzstr
275 + ", outtz=" + outtzid
);
277 errln((UnicodeString
)"Canonical round trip failed; tz=" + *tzid
278 + ", locale=" + LOCALES
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
279 + ", time=" + DATES
[datidx
] + ", str=" + tzstr
280 + ", outtz=" + outtzid
);
282 if (U_FAILURE(status
)) {
283 errln("hasEquivalentTransitions failed");
284 status
= U_ZERO_ERROR
;
290 UBool isOffsetFormat
= (*PATTERNS
[patidx
] == 'Z'
291 || *PATTERNS
[patidx
] == 'O'
292 || *PATTERNS
[patidx
] == 'X'
293 || *PATTERNS
[patidx
] == 'x');
294 UBool minutesOffset
= FALSE
;
295 if (*PATTERNS
[patidx
] == 'X' || *PATTERNS
[patidx
] == 'x') {
296 minutesOffset
= (uprv_strlen(PATTERNS
[patidx
]) <= 3);
299 if (!isOffsetFormat
) {
300 // Check if localized GMT format is used as a fallback of name styles
301 int32_t numDigits
= 0;
302 for (int n
= 0; n
< tzstr
.length(); n
++) {
303 if (u_isdigit(tzstr
.charAt(n
))) {
307 isOffsetFormat
= (numDigits
> 0);
309 if (isOffsetFormat
|| tzstr
== localGMTString
) {
310 // Localized GMT or ISO: total offset (raw + dst) must be preserved.
311 int32_t inOffset
= inRaw
+ inDst
;
312 int32_t outOffset
= outRaw
+ outDst
;
313 int32_t diff
= outOffset
- inOffset
;
315 diff
= (diff
/ 60000) * 60000;
318 errln((UnicodeString
)"Offset round trip failed; tz=" + *tzid
319 + ", locale=" + LOCALES
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
320 + ", time=" + DATES
[datidx
] + ", str=" + tzstr
321 + ", inOffset=" + inOffset
+ ", outOffset=" + outOffset
);
324 // Specific or generic: raw offset must be preserved.
325 if (inRaw
!= outRaw
) {
326 errln((UnicodeString
)"Raw offset round trip failed; tz=" + *tzid
327 + ", locale=" + LOCALES
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
328 + ", time=" + DATES
[datidx
] + ", str=" + tzstr
329 + ", inRawOffset=" + inRaw
+ ", outRawOffset=" + outRaw
);
344 // Special exclusions in TestTimeZoneRoundTrip.
345 // These special cases do not round trip time as designed.
346 static UBool
isSpecialTimeRoundTripCase(const char* loc
,
347 const UnicodeString
& id
,
356 {NULL
, "Asia/Chita", "zzzz", 1414252800000.0},
357 {NULL
, "Asia/Chita", "vvvv", 1414252800000.0},
358 {NULL
, "Asia/Srednekolymsk", "zzzz", 1414241999999.0},
359 {NULL
, "Asia/Srednekolymsk", "vvvv", 1414241999999.0},
360 {NULL
, NULL
, NULL
, U_DATE_MIN
}
363 UBool isExcluded
= FALSE
;
364 for (int32_t i
= 0; EXCLUSIONS
[i
].id
!= NULL
; i
++) {
365 if (EXCLUSIONS
[i
].loc
== NULL
|| uprv_strcmp(loc
, EXCLUSIONS
[i
].loc
) == 0) {
366 if (id
.compare(EXCLUSIONS
[i
].id
) == 0) {
367 if (EXCLUSIONS
[i
].pattern
== NULL
|| uprv_strcmp(pattern
, EXCLUSIONS
[i
].pattern
) == 0) {
368 if (EXCLUSIONS
[i
].time
== U_DATE_MIN
|| EXCLUSIONS
[i
].time
== time
) {
378 // LocaleData. Somewhat misnamed. For TestTimeZoneRoundTrip, specifies the locales and patterns
379 // to be tested, and provides an iterator over these for the multi-threaded test
380 // functions to pick up the next combination to be tested.
382 // A single global instance of this struct is shared among all
385 // "locales" is an array of locales to be tested.
386 // PATTERNS (a global) is an array of patterns to be tested for each locale.
387 // "localeIndex" and "patternIndex" keep track of the iteration through the above.
388 // Each of the parallel test threads calls LocaleData::nextTest() in a loop
389 // to find out what to test next. It must be thread safe.
392 int32_t patternIndex
;
394 UDate times
[UPRV_LENGTHOF(PATTERNS
)]; // Performance data, Elapsed time for each pattern.
395 const Locale
* locales
;
401 LocaleData() : localeIndex(0), patternIndex(0), testCounts(0), locales(NULL
),
402 nLocales(0), START_TIME(0), END_TIME(0), numDone(0) {
403 for (int i
=0; i
<UPRV_LENGTHOF(times
); i
++) {
408 void resetTestIteration() {
410 patternIndex
= UPRV_LENGTHOF(PATTERNS
);
414 UBool
nextTest(int32_t &rLocaleIndex
, int32_t &rPatternIndex
) {
416 if (patternIndex
>= UPRV_LENGTHOF(PATTERNS
) - 1) {
417 if (localeIndex
>= nLocales
- 1) {
424 rLocaleIndex
= localeIndex
;
425 rPatternIndex
= patternIndex
;
430 void addTime(UDate amount
, int32_t patIdx
) {
432 U_ASSERT(patIdx
< UPRV_LENGTHOF(PATTERNS
));
433 times
[patIdx
] += amount
;
437 static LocaleData
*gLocaleData
= NULL
;
440 TimeZoneFormatTest::TestTimeRoundTrip(void) {
441 UErrorCode status
= U_ZERO_ERROR
;
442 LocalPointer
<Calendar
> cal(Calendar::createInstance(TimeZone::createTimeZone((UnicodeString
) "UTC"), status
));
443 if (U_FAILURE(status
)) {
444 dataerrln("Calendar::createInstance failed: %s", u_errorName(status
));
448 const char* testAllProp
= getProperty("TimeZoneRoundTripAll");
449 UBool bTestAll
= (testAllProp
&& uprv_strcmp(testAllProp
, "true") == 0);
451 UDate START_TIME
, END_TIME
;
452 if (bTestAll
|| !quick
) {
453 cal
->set(1900, UCAL_JANUARY
, 1);
455 cal
->set(1999, UCAL_JANUARY
, 1);
457 START_TIME
= cal
->getTime(status
);
459 cal
->set(2022, UCAL_JANUARY
, 1);
460 END_TIME
= cal
->getTime(status
);
462 if (U_FAILURE(status
)) {
463 errln("getTime failed");
467 LocaleData localeData
;
468 gLocaleData
= &localeData
;
470 // Set up test locales
471 const Locale locales1
[] = {Locale("en")};
472 const Locale locales2
[] = {
473 Locale("ar_EG"), Locale("bg_BG"), Locale("ca_ES"), Locale("da_DK"), Locale("de"),
474 Locale("de_DE"), Locale("el_GR"), Locale("en"), Locale("en_AU"), Locale("en_CA"),
475 Locale("en_US"), Locale("es"), Locale("es_ES"), Locale("es_MX"), Locale("fi_FI"),
476 Locale("fr"), Locale("fr_CA"), Locale("fr_FR"), Locale("he_IL"), Locale("hu_HU"),
477 Locale("it"), Locale("it_IT"), Locale("ja"), Locale("ja_JP"), Locale("ko"),
478 Locale("ko_KR"), Locale("nb_NO"), Locale("nl_NL"), Locale("nn_NO"), Locale("pl_PL"),
479 Locale("pt"), Locale("pt_BR"), Locale("pt_PT"), Locale("ru_RU"), Locale("sv_SE"),
480 Locale("th_TH"), Locale("tr_TR"), Locale("zh"), Locale("zh_Hans"), Locale("zh_Hans_CN"),
481 Locale("zh_Hant"), Locale("zh_Hant_TW")
485 gLocaleData
->locales
= Locale::getAvailableLocales(gLocaleData
->nLocales
);
487 gLocaleData
->locales
= locales1
;
488 gLocaleData
->nLocales
= UPRV_LENGTHOF(locales1
);
490 gLocaleData
->locales
= locales2
;
491 gLocaleData
->nLocales
= UPRV_LENGTHOF(locales2
);
494 gLocaleData
->START_TIME
= START_TIME
;
495 gLocaleData
->END_TIME
= END_TIME
;
496 gLocaleData
->resetTestIteration();
498 // start IntlTest.threadCount threads, each running the function RunTimeRoundTripTests().
500 ThreadPool
<TimeZoneFormatTest
> threads(this, threadCount
, &TimeZoneFormatTest::RunTimeRoundTripTests
);
501 threads
.start(); // Start all threads.
502 threads
.join(); // Wait for all threads to finish.
505 logln("### Elapsed time by patterns ###");
506 for (int32_t i
= 0; i
< UPRV_LENGTHOF(PATTERNS
); i
++) {
507 logln(UnicodeString("") + gLocaleData
->times
[i
] + "ms (" + PATTERNS
[i
] + ")");
508 total
+= gLocaleData
->times
[i
];
510 logln((UnicodeString
) "Total: " + total
+ "ms");
511 logln((UnicodeString
) "Iteration: " + gLocaleData
->testCounts
);
515 // TimeZoneFormatTest::RunTimeRoundTripTests()
516 // This function loops, running time zone format round trip test cases until there are no more, then returns.
517 // Threading: multiple invocations of this function are started in parallel
518 // by TimeZoneFormatTest::TestTimeRoundTrip()
520 void TimeZoneFormatTest::RunTimeRoundTripTests(int32_t threadNumber
) {
521 UErrorCode status
= U_ZERO_ERROR
;
522 UBool REALLY_VERBOSE
= FALSE
;
524 // These patterns are ambiguous at DST->STD local time overlap
525 const char* AMBIGUOUS_DST_DECESSION
[] = { "v", "vvvv", "V", "VV", "VVV", "VVVV", 0 };
527 // These patterns are ambiguous at STD->STD/DST->DST local time overlap
528 const char* AMBIGUOUS_NEGATIVE_SHIFT
[] = { "z", "zzzz", "v", "vvvv", "V", "VV", "VVV", "VVVV", 0 };
530 // These patterns only support integer minutes offset
531 const char* MINUTES_OFFSET
[] = { "X", "XX", "XXX", "x", "xx", "xxx", 0 };
533 // Workaround for #6338
534 //UnicodeString BASEPATTERN("yyyy-MM-dd'T'HH:mm:ss.SSS");
535 UnicodeString
BASEPATTERN("yyyy.MM.dd HH:mm:ss.SSS");
537 // timer for performance analysis
540 UBool expectedRoundTrip
[4];
543 StringEnumeration
*tzids
= TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL
, NULL
, NULL
, status
);
544 if (U_FAILURE(status
)) {
545 if (status
== U_MISSING_RESOURCE_ERROR
) {
546 // This error is generally caused by data not being present.
547 dataerrln("TimeZone::createTimeZoneIDEnumeration failed - %s", u_errorName(status
));
549 errln("TimeZone::createTimeZoneIDEnumeration failed: %s", u_errorName(status
));
557 while (gLocaleData
->nextTest(locidx
, patidx
)) {
559 UnicodeString
pattern(BASEPATTERN
);
560 pattern
.append(" ").append(PATTERNS
[patidx
]);
561 logln(" Thread %d, Locale %s, Pattern %s",
562 threadNumber
, gLocaleData
->locales
[locidx
].getName(), CStr(pattern
)());
564 SimpleDateFormat
*sdf
= new SimpleDateFormat(pattern
, gLocaleData
->locales
[locidx
], status
);
565 if (U_FAILURE(status
)) {
566 errcheckln(status
, (UnicodeString
) "new SimpleDateFormat failed for pattern " +
567 pattern
+ " for locale " + gLocaleData
->locales
[locidx
].getName() + " - " + u_errorName(status
));
568 status
= U_ZERO_ERROR
;
572 UBool minutesOffset
= contains(MINUTES_OFFSET
, PATTERNS
[patidx
]);
574 tzids
->reset(status
);
575 const UnicodeString
*tzid
;
577 timer
= Calendar::getNow();
579 while ((tzid
= tzids
->snext(status
))) {
580 if (uprv_strcmp(PATTERNS
[patidx
], "V") == 0) {
581 // Some zones do not have short ID assigned, such as Asia/Riyadh87.
582 // The time roundtrip will fail for such zones with pattern "V" (short zone ID).
583 // This is expected behavior.
584 const UChar
* shortZoneID
= ZoneMeta::getShortID(*tzid
);
585 if (shortZoneID
== NULL
) {
588 } else if (uprv_strcmp(PATTERNS
[patidx
], "VVV") == 0) {
589 // Some zones are not associated with any region, such as Etc/GMT+8.
590 // The time roundtrip will fail for such zone with pattern "VVV" (exemplar location).
591 // This is expected behavior.
592 if (tzid
->indexOf((UChar
)0x2F) < 0 || tzid
->indexOf(ETC_SLASH
, -1, 0) >= 0
593 || tzid
->indexOf(SYSTEMV_SLASH
, -1, 0) >= 0 || tzid
->indexOf(RIYADH8
, -1, 0) >= 0) {
598 if (*tzid
== "Pacific/Apia" && uprv_strcmp(PATTERNS
[patidx
], "vvvv") == 0
599 && logKnownIssue("11052", "Ambiguous zone name - Samoa Time")) {
602 // skip some zone/format combinations until we have a metazone for them and/or
603 // better fallback zone formatting...
604 if ((*tzid
== "America/Punta_Arenas" || *tzid
== "Antarctica/Palmer") &&
605 uprv_strcmp(PATTERNS
[patidx
], "zzzz") == 0) {
609 BasicTimeZone
*tz
= (BasicTimeZone
*) TimeZone::createTimeZone(*tzid
);
610 sdf
->setTimeZone(*tz
);
612 UDate t
= gLocaleData
->START_TIME
;
613 TimeZoneTransition tzt
;
614 UBool tztAvail
= FALSE
;
617 while (t
< gLocaleData
->END_TIME
) {
620 expectedRoundTrip
[0] = TRUE
;
623 int32_t fromOffset
= tzt
.getFrom()->getRawOffset() + tzt
.getFrom()->getDSTSavings();
624 int32_t toOffset
= tzt
.getTo()->getRawOffset() + tzt
.getTo()->getDSTSavings();
625 int32_t delta
= toOffset
- fromOffset
;
627 UBool isDstDecession
= tzt
.getFrom()->getDSTSavings() > 0 && tzt
.getTo()->getDSTSavings() == 0;
628 testTimes
[0] = t
+ delta
- 1;
629 expectedRoundTrip
[0] = TRUE
;
630 testTimes
[1] = t
+ delta
;
631 expectedRoundTrip
[1] = isDstDecession
?
632 !contains(AMBIGUOUS_DST_DECESSION
, PATTERNS
[patidx
]) :
633 !contains(AMBIGUOUS_NEGATIVE_SHIFT
, PATTERNS
[patidx
]);
634 testTimes
[2] = t
- 1;
635 expectedRoundTrip
[2] = isDstDecession
?
636 !contains(AMBIGUOUS_DST_DECESSION
, PATTERNS
[patidx
]) :
637 !contains(AMBIGUOUS_NEGATIVE_SHIFT
, PATTERNS
[patidx
]);
639 expectedRoundTrip
[3] = TRUE
;
642 testTimes
[0] = t
- 1;
643 expectedRoundTrip
[0] = TRUE
;
645 expectedRoundTrip
[1] = TRUE
;
649 for (int32_t testidx
= 0; testidx
< testLen
; testidx
++) {
651 // reduce regular test time
652 if (!expectedRoundTrip
[testidx
]) {
659 gLocaleData
->testCounts
++;
663 FieldPosition
fpos(FieldPosition::DONT_CARE
);
664 sdf
->format(testTimes
[testidx
], text
, fpos
);
666 UDate parsedDate
= sdf
->parse(text
, status
);
667 if (U_FAILURE(status
)) {
668 errln((UnicodeString
) "Parse failure for text=" + text
+ ", tzid=" + *tzid
+ ", locale=" + gLocaleData
->locales
[locidx
].getName()
669 + ", pattern=" + PATTERNS
[patidx
] + ", time=" + testTimes
[testidx
]);
670 status
= U_ZERO_ERROR
;
674 int32_t timeDiff
= (int32_t)(parsedDate
- testTimes
[testidx
]);
675 UBool bTimeMatch
= minutesOffset
?
676 (timeDiff
/60000)*60000 == 0 : timeDiff
== 0;
678 UnicodeString msg
= (UnicodeString
) "Time round trip failed for " + "tzid=" + *tzid
679 + ", locale=" + gLocaleData
->locales
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
680 + ", text=" + text
+ ", time=" + testTimes
[testidx
] + ", restime=" + parsedDate
+ ", diff=" + (parsedDate
- testTimes
[testidx
]);
681 // Timebomb for TZData update
682 if (expectedRoundTrip
[testidx
]
683 && !isSpecialTimeRoundTripCase(gLocaleData
->locales
[locidx
].getName(), *tzid
,
684 PATTERNS
[patidx
], testTimes
[testidx
])) {
685 errln((UnicodeString
) "FAIL: " + msg
);
686 } else if (REALLY_VERBOSE
) {
691 tztAvail
= tz
->getNextTransition(t
, FALSE
, tzt
);
696 // Test the date in the middle of two transitions.
697 t
+= (int64_t) ((tzt
.getTime() - t
) / 2);
706 UDate elapsedTime
= Calendar::getNow() - timer
;
707 gLocaleData
->addTime(elapsedTime
, patidx
);
718 UTimeZoneFormatStyle style
;
719 uint32_t parseOptions
;
720 const char* expected
;
722 UTimeZoneFormatTimeType timeType
;
726 TimeZoneFormatTest::TestParse(void) {
727 const ParseTestData DATA
[] = {
728 // text inPos locale style
729 // parseOptions expected outPos timeType
730 {"Z", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL
,
731 UTZFMT_PARSE_OPTION_NONE
, "Etc/GMT", 1, UTZFMT_TIME_TYPE_UNKNOWN
},
733 {"Z", 0, "en_US", UTZFMT_STYLE_SPECIFIC_LONG
,
734 UTZFMT_PARSE_OPTION_NONE
, "Etc/GMT", 1, UTZFMT_TIME_TYPE_UNKNOWN
},
736 {"Zambia time", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL
,
737 UTZFMT_PARSE_OPTION_ALL_STYLES
, "Etc/GMT", 1, UTZFMT_TIME_TYPE_UNKNOWN
},
739 {"Zambia time", 0, "en_US", UTZFMT_STYLE_GENERIC_LOCATION
,
740 UTZFMT_PARSE_OPTION_NONE
, "Africa/Lusaka", 11, UTZFMT_TIME_TYPE_UNKNOWN
},
742 {"Zambia time", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
,
743 UTZFMT_PARSE_OPTION_ALL_STYLES
, "Africa/Lusaka", 11, UTZFMT_TIME_TYPE_UNKNOWN
},
745 {"+00:00", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL
,
746 UTZFMT_PARSE_OPTION_NONE
, "Etc/GMT", 6, UTZFMT_TIME_TYPE_UNKNOWN
},
748 {"-01:30:45", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL
,
749 UTZFMT_PARSE_OPTION_NONE
, "GMT-01:30:45", 9, UTZFMT_TIME_TYPE_UNKNOWN
},
751 {"-7", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
,
752 UTZFMT_PARSE_OPTION_NONE
, "GMT-07:00", 2, UTZFMT_TIME_TYPE_UNKNOWN
},
754 {"-2222", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
,
755 UTZFMT_PARSE_OPTION_NONE
, "GMT-22:22", 5, UTZFMT_TIME_TYPE_UNKNOWN
},
757 {"-3333", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
,
758 UTZFMT_PARSE_OPTION_NONE
, "GMT-03:33", 4, UTZFMT_TIME_TYPE_UNKNOWN
},
760 {"XXX+01:30YYY", 3, "en_US", UTZFMT_STYLE_LOCALIZED_GMT
,
761 UTZFMT_PARSE_OPTION_NONE
, "GMT+01:30", 9, UTZFMT_TIME_TYPE_UNKNOWN
},
763 {"GMT0", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT
,
764 UTZFMT_PARSE_OPTION_NONE
, "Etc/GMT", 3, UTZFMT_TIME_TYPE_UNKNOWN
},
766 {"EST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT
,
767 UTZFMT_PARSE_OPTION_NONE
, "America/New_York", 3, UTZFMT_TIME_TYPE_STANDARD
},
769 {"ESTx", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT
,
770 UTZFMT_PARSE_OPTION_NONE
, "America/New_York", 3, UTZFMT_TIME_TYPE_STANDARD
},
772 {"EDTx", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT
,
773 UTZFMT_PARSE_OPTION_NONE
, "America/New_York", 3, UTZFMT_TIME_TYPE_DAYLIGHT
},
775 {"EST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_LONG
,
776 UTZFMT_PARSE_OPTION_NONE
, NULL
, 0, UTZFMT_TIME_TYPE_UNKNOWN
},
778 {"EST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_LONG
,
779 UTZFMT_PARSE_OPTION_ALL_STYLES
, "America/New_York", 3, UTZFMT_TIME_TYPE_STANDARD
},
781 {"EST", 0, "en_CA", UTZFMT_STYLE_SPECIFIC_SHORT
,
782 UTZFMT_PARSE_OPTION_NONE
, "America/Toronto", 3, UTZFMT_TIME_TYPE_STANDARD
},
784 {"CST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT
,
785 UTZFMT_PARSE_OPTION_NONE
, "America/Chicago", 3, UTZFMT_TIME_TYPE_STANDARD
},
787 {"CST", 0, "en_GB", UTZFMT_STYLE_SPECIFIC_SHORT
,
788 UTZFMT_PARSE_OPTION_NONE
, NULL
, 0, UTZFMT_TIME_TYPE_UNKNOWN
},
790 {"CST", 0, "en_GB", UTZFMT_STYLE_SPECIFIC_SHORT
,
791 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS
, "America/Chicago", 3, UTZFMT_TIME_TYPE_STANDARD
},
793 {"--CST--", 2, "en_GB", UTZFMT_STYLE_SPECIFIC_SHORT
,
794 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS
, "America/Chicago", 5, UTZFMT_TIME_TYPE_STANDARD
},
796 {"CST", 0, "zh_CN", UTZFMT_STYLE_SPECIFIC_SHORT
,
797 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS
, "Asia/Shanghai", 3, UTZFMT_TIME_TYPE_STANDARD
},
799 {"AEST", 0, "en_AU", UTZFMT_STYLE_SPECIFIC_SHORT
,
800 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS
, "Australia/Sydney", 4, UTZFMT_TIME_TYPE_STANDARD
},
802 {"AST", 0, "ar_SA", UTZFMT_STYLE_SPECIFIC_SHORT
,
803 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS
, "Asia/Riyadh", 3, UTZFMT_TIME_TYPE_STANDARD
},
805 {"AQTST", 0, "en", UTZFMT_STYLE_SPECIFIC_LONG
,
806 UTZFMT_PARSE_OPTION_NONE
, NULL
, 0, UTZFMT_TIME_TYPE_UNKNOWN
},
808 {"AQTST", 0, "en", UTZFMT_STYLE_SPECIFIC_LONG
,
809 UTZFMT_PARSE_OPTION_ALL_STYLES
, NULL
, 0, UTZFMT_TIME_TYPE_UNKNOWN
},
811 {"AQTST", 0, "en", UTZFMT_STYLE_SPECIFIC_LONG
,
812 UTZFMT_PARSE_OPTION_ALL_STYLES
| UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS
, "Asia/Aqtobe", 5, UTZFMT_TIME_TYPE_DAYLIGHT
},
814 {NULL
, 0, NULL
, UTZFMT_STYLE_GENERIC_LOCATION
,
815 UTZFMT_PARSE_OPTION_NONE
, NULL
, 0, UTZFMT_TIME_TYPE_UNKNOWN
}
818 for (int32_t i
= 0; DATA
[i
].text
; i
++) {
819 UErrorCode status
= U_ZERO_ERROR
;
820 LocalPointer
<TimeZoneFormat
> tzfmt(TimeZoneFormat::createInstance(Locale(DATA
[i
].locale
), status
));
821 if (U_FAILURE(status
)) {
822 dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status
));
825 UTimeZoneFormatTimeType ttype
= UTZFMT_TIME_TYPE_UNKNOWN
;
826 ParsePosition
pos(DATA
[i
].inPos
);
827 TimeZone
* tz
= tzfmt
->parse(DATA
[i
].style
, DATA
[i
].text
, pos
, DATA
[i
].parseOptions
, &ttype
);
829 UnicodeString errMsg
;
833 if (outID
!= UnicodeString(DATA
[i
].expected
)) {
834 errMsg
= (UnicodeString
)"Time zone ID: " + outID
+ " - expected: " + DATA
[i
].expected
;
835 } else if (pos
.getIndex() != DATA
[i
].outPos
) {
836 errMsg
= (UnicodeString
)"Parsed pos: " + pos
.getIndex() + " - expected: " + DATA
[i
].outPos
;
837 } else if (ttype
!= DATA
[i
].timeType
) {
838 errMsg
= (UnicodeString
)"Time type: " + ttype
+ " - expected: " + DATA
[i
].timeType
;
842 if (DATA
[i
].expected
) {
843 errMsg
= (UnicodeString
)"Parse failure - expected: " + DATA
[i
].expected
;
846 if (errMsg
.length() > 0) {
847 errln((UnicodeString
)"Fail: " + errMsg
+ " [text=" + DATA
[i
].text
+ ", pos=" + DATA
[i
].inPos
+ ", style=" + DATA
[i
].style
+ "]");
853 TimeZoneFormatTest::TestISOFormat(void) {
854 const int32_t OFFSET
[] = {
859 -77777, // -1m 17.777s
863 -37800000, // -10h 30m
864 -37845000, // -10h 30m 45s
868 const char* ISO_STR
[][11] = {
871 "Z", "Z", "Z", "Z", "Z",
872 "+00", "+0000", "+00:00", "+0000", "+00:00",
877 "Z", "Z", "Z", "Z", "Z",
878 "+00", "+0000", "+00:00", "+0000", "+00:00",
883 "Z", "Z", "Z", "-000059", "-00:00:59",
884 "+00", "+0000", "+00:00", "-000059", "-00:00:59",
889 "+0001", "+0001", "+00:01", "+0001", "+00:01",
890 "+0001", "+0001", "+00:01", "+0001", "+00:01",
895 "-0001", "-0001", "-00:01", "-000117", "-00:01:17",
896 "-0001", "-0001", "-00:01", "-000117", "-00:01:17",
901 "+0030", "+0030", "+00:30", "+0030", "+00:30",
902 "+0030", "+0030", "+00:30", "+0030", "+00:30",
907 "-01", "-0100", "-01:00", "-0100", "-01:00",
908 "-01", "-0100", "-01:00", "-0100", "-01:00",
913 "+10", "+1000", "+10:00", "+1000", "+10:00",
914 "+10", "+1000", "+10:00", "+1000", "+10:00",
919 "-1030", "-1030", "-10:30", "-1030", "-10:30",
920 "-1030", "-1030", "-10:30", "-1030", "-10:30",
925 "-1030", "-1030", "-10:30", "-103045", "-10:30:45",
926 "-1030", "-1030", "-10:30", "-103045", "-10:30:45",
937 const char* PATTERN
[] = {
938 "X", "XX", "XXX", "XXXX", "XXXXX",
939 "x", "xx", "xxx", "xxxx", "xxxxx",
940 "Z", // equivalent to "xxxx"
944 const int32_t MIN_OFFSET_UNIT
[] = {
945 60000, 60000, 60000, 1000, 1000,
946 60000, 60000, 60000, 1000, 1000,
951 UErrorCode status
= U_ZERO_ERROR
;
952 LocalPointer
<SimpleDateFormat
> sdf(new SimpleDateFormat(status
), status
);
953 if (U_FAILURE(status
)) {
954 dataerrln("Fail new SimpleDateFormat: %s", u_errorName(status
));
957 UDate d
= Calendar::getNow();
959 for (uint32_t i
= 0; i
< UPRV_LENGTHOF(OFFSET
); i
++) {
960 SimpleTimeZone
* tz
= new SimpleTimeZone(OFFSET
[i
], UnicodeString("Zone Offset:") + OFFSET
[i
] + "ms");
961 sdf
->adoptTimeZone(tz
);
962 for (int32_t j
= 0; PATTERN
[j
] != 0; j
++) {
963 sdf
->applyPattern(UnicodeString(PATTERN
[j
]));
964 UnicodeString result
;
965 sdf
->format(d
, result
);
968 if (result
!= UnicodeString(ISO_STR
[i
][j
])) {
969 errln((UnicodeString
)"FAIL: pattern=" + PATTERN
[j
] + ", offset=" + OFFSET
[i
] + " -> "
970 + result
+ " (expected: " + ISO_STR
[i
][j
] + ")");
973 // Offset out of range
974 // Note: for now, there is no way to propagate the error status through
975 // the SimpleDateFormat::format above.
976 if (result
.length() > 0) {
977 errln((UnicodeString
)"FAIL: Non-Empty result for pattern=" + PATTERN
[j
] + ", offset=" + OFFSET
[i
]
978 + " (expected: empty result)");
985 LocalPointer
<Calendar
> outcal(Calendar::createInstance(status
));
986 if (U_FAILURE(status
)) {
987 dataerrln("Fail new Calendar: %s", u_errorName(status
));
990 for (int32_t i
= 0; ISO_STR
[i
][0] != NULL
; i
++) {
991 for (int32_t j
= 0; PATTERN
[j
] != 0; j
++) {
992 if (ISO_STR
[i
][j
] == 0) {
995 ParsePosition
pos(0);
996 SimpleTimeZone
* bogusTZ
= new SimpleTimeZone(-1, UnicodeString("Zone Offset: -1ms"));
997 outcal
->adoptTimeZone(bogusTZ
);
998 sdf
->applyPattern(PATTERN
[j
]);
1000 sdf
->parse(UnicodeString(ISO_STR
[i
][j
]), *(outcal
.getAlias()), pos
);
1002 if (pos
.getIndex() != (int32_t)uprv_strlen(ISO_STR
[i
][j
])) {
1003 errln((UnicodeString
)"FAIL: Failed to parse the entire input string: " + ISO_STR
[i
][j
]);
1006 const TimeZone
& outtz
= outcal
->getTimeZone();
1007 int32_t outOffset
= outtz
.getRawOffset();
1008 int32_t adjustedOffset
= OFFSET
[i
] / MIN_OFFSET_UNIT
[j
] * MIN_OFFSET_UNIT
[j
];
1009 if (outOffset
!= adjustedOffset
) {
1010 errln((UnicodeString
)"FAIL: Incorrect offset:" + outOffset
+ "ms for input string: " + ISO_STR
[i
][j
]
1011 + " (expected:" + adjustedOffset
+ "ms)");
1022 UTimeZoneFormatStyle style
;
1023 const char* expected
;
1024 UTimeZoneFormatTimeType timeType
;
1028 TimeZoneFormatTest::TestFormat(void) {
1029 UDate dateJan
= 1358208000000.0; // 2013-01-15T00:00:00Z
1030 UDate dateJul
= 1373846400000.0; // 2013-07-15T00:00:00Z
1032 const FormatTestData DATA
[] = {
1035 "America/Los_Angeles",
1037 UTZFMT_STYLE_GENERIC_LOCATION
,
1039 UTZFMT_TIME_TYPE_UNKNOWN
1043 "America/Los_Angeles",
1045 UTZFMT_STYLE_GENERIC_LONG
,
1047 UTZFMT_TIME_TYPE_UNKNOWN
1051 "America/Los_Angeles",
1053 UTZFMT_STYLE_SPECIFIC_LONG
,
1054 "Pacific Standard Time",
1055 UTZFMT_TIME_TYPE_STANDARD
1059 "America/Los_Angeles",
1061 UTZFMT_STYLE_SPECIFIC_LONG
,
1062 "Pacific Daylight Time",
1063 UTZFMT_TIME_TYPE_DAYLIGHT
1067 "America/Los_Angeles",
1069 UTZFMT_STYLE_ZONE_ID
,
1070 "America/Los_Angeles",
1071 UTZFMT_TIME_TYPE_UNKNOWN
1075 "America/Los_Angeles",
1077 UTZFMT_STYLE_ZONE_ID_SHORT
,
1079 UTZFMT_TIME_TYPE_UNKNOWN
1083 "America/Los_Angeles",
1085 UTZFMT_STYLE_EXEMPLAR_LOCATION
,
1087 UTZFMT_TIME_TYPE_UNKNOWN
1094 UTZFMT_STYLE_GENERIC_LONG
,
1095 "\\u65E5\\u672C\\u6A19\\u6E96\\u6642",
1096 UTZFMT_TIME_TYPE_UNKNOWN
1099 {0, 0, 0.0, UTZFMT_STYLE_GENERIC_LOCATION
, 0, UTZFMT_TIME_TYPE_UNKNOWN
}
1102 for (int32_t i
= 0; DATA
[i
].locale
; i
++) {
1103 UErrorCode status
= U_ZERO_ERROR
;
1104 LocalPointer
<TimeZoneFormat
> tzfmt(TimeZoneFormat::createInstance(Locale(DATA
[i
].locale
), status
));
1105 if (U_FAILURE(status
)) {
1106 dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status
));
1110 LocalPointer
<TimeZone
> tz(TimeZone::createTimeZone(DATA
[i
].tzid
));
1112 UTimeZoneFormatTimeType timeType
;
1114 tzfmt
->format(DATA
[i
].style
, *(tz
.getAlias()), DATA
[i
].date
, out
, &timeType
);
1115 UnicodeString
expected(DATA
[i
].expected
, -1, US_INV
);
1116 expected
= expected
.unescape();
1118 assertEquals(UnicodeString("Format result for ") + DATA
[i
].tzid
+ " (Test Case " + i
+ ")", expected
, out
);
1119 if (DATA
[i
].timeType
!= timeType
) {
1120 dataerrln(UnicodeString("Formatted time zone type (Test Case ") + i
+ "), returned="
1121 + timeType
+ ", expected=" + DATA
[i
].timeType
);
1127 TimeZoneFormatTest::TestFormatTZDBNames(void) {
1128 UDate dateJan
= 1358208000000.0; // 2013-01-15T00:00:00Z
1129 UDate dateJul
= 1373846400000.0; // 2013-07-15T00:00:00Z
1131 const FormatTestData DATA
[] = {
1136 UTZFMT_STYLE_SPECIFIC_SHORT
,
1138 UTZFMT_TIME_TYPE_STANDARD
1144 UTZFMT_STYLE_SPECIFIC_SHORT
,
1146 UTZFMT_TIME_TYPE_STANDARD
1152 UTZFMT_STYLE_SPECIFIC_SHORT
,
1154 UTZFMT_TIME_TYPE_STANDARD
1158 "America/Los_Angeles",
1160 UTZFMT_STYLE_SPECIFIC_LONG
,
1161 "GMT-07:00", // No long display names
1162 UTZFMT_TIME_TYPE_DAYLIGHT
1166 "America/Los_Angeles",
1168 UTZFMT_STYLE_SPECIFIC_SHORT
,
1170 UTZFMT_TIME_TYPE_DAYLIGHT
1176 UTZFMT_STYLE_SPECIFIC_SHORT
,
1178 UTZFMT_TIME_TYPE_DAYLIGHT
1184 UTZFMT_STYLE_SPECIFIC_SHORT
,
1186 UTZFMT_TIME_TYPE_STANDARD
1189 {0, 0, 0.0, UTZFMT_STYLE_GENERIC_LOCATION
, 0, UTZFMT_TIME_TYPE_UNKNOWN
}
1192 for (int32_t i
= 0; DATA
[i
].locale
; i
++) {
1193 UErrorCode status
= U_ZERO_ERROR
;
1194 Locale
loc(DATA
[i
].locale
);
1195 LocalPointer
<TimeZoneFormat
> tzfmt(TimeZoneFormat::createInstance(loc
, status
));
1196 if (U_FAILURE(status
)) {
1197 dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status
));
1200 TimeZoneNames
*tzdbNames
= TimeZoneNames::createTZDBInstance(loc
, status
);
1201 if (U_FAILURE(status
)) {
1202 dataerrln("Fail TimeZoneNames::createTZDBInstance: %s", u_errorName(status
));
1205 tzfmt
->adoptTimeZoneNames(tzdbNames
);
1207 LocalPointer
<TimeZone
> tz(TimeZone::createTimeZone(DATA
[i
].tzid
));
1209 UTimeZoneFormatTimeType timeType
;
1211 tzfmt
->format(DATA
[i
].style
, *(tz
.getAlias()), DATA
[i
].date
, out
, &timeType
);
1212 UnicodeString
expected(DATA
[i
].expected
, -1, US_INV
);
1213 expected
= expected
.unescape();
1215 assertEquals(UnicodeString("Format result for ") + DATA
[i
].tzid
+ " (Test Case " + i
+ ")", expected
, out
);
1216 if (DATA
[i
].timeType
!= timeType
) {
1217 dataerrln(UnicodeString("Formatted time zone type (Test Case ") + i
+ "), returned="
1218 + timeType
+ ", expected=" + DATA
[i
].timeType
);
1224 TimeZoneFormatTest::TestFormatCustomZone(void) {
1228 const char* expected
;
1230 { "abc", 3600000, "GMT+01:00" }, // unknown ID
1231 { "$abc", -3600000, "GMT-01:00" }, // unknown, with ASCII variant char '$'
1232 { "\\u00c1\\u00df\\u00c7", 5400000, "GMT+01:30"}, // unknown, with non-ASCII chars
1236 UDate now
= Calendar::getNow();
1238 for (int32_t i
= 0; ; i
++) {
1239 const char *id
= TESTDATA
[i
].id
;
1243 UnicodeString tzid
= UnicodeString(id
, -1, US_INV
).unescape();
1244 SimpleTimeZone
tz(TESTDATA
[i
].offset
, tzid
);
1246 UErrorCode status
= U_ZERO_ERROR
;
1247 LocalPointer
<TimeZoneFormat
> tzfmt(TimeZoneFormat::createInstance(Locale("en"), status
));
1248 if (tzfmt
.isNull()) {
1249 dataerrln("FAIL: TimeZoneFormat::createInstance failed for en");
1252 UnicodeString tzstr
;
1253 UnicodeString expected
= UnicodeString(TESTDATA
[i
].expected
, -1, US_INV
).unescape();
1255 tzfmt
->format(UTZFMT_STYLE_SPECIFIC_LONG
, tz
, now
, tzstr
, NULL
);
1256 assertEquals(UnicodeString("Format result for ") + tzid
, expected
, tzstr
);
1261 #endif /* #if !UCONFIG_NO_FORMATTING */