2 *******************************************************************************
3 * Copyright (C) 2007-2015, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
7 #include "unicode/utypes.h"
9 #if !UCONFIG_NO_FORMATTING
13 #include "unicode/timezone.h"
14 #include "unicode/simpletz.h"
15 #include "unicode/calendar.h"
16 #include "unicode/strenum.h"
17 #include "unicode/smpdtfmt.h"
18 #include "unicode/uchar.h"
19 #include "unicode/basictz.h"
20 #include "unicode/tzfmt.h"
21 #include "unicode/localpointer.h"
26 #include "simplethread.h"
30 static const char* PATTERNS
[] = {
33 "Z", // equivalent to "xxxx"
34 "ZZZZ", // equivalent to "OOOO"
55 static const UChar ETC_UNKNOWN
[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x6E, 0x6B, 0x6E, 0x6F, 0x77, 0x6E, 0};
57 static const UChar ETC_SLASH
[] = { 0x45, 0x74, 0x63, 0x2F, 0 }; // "Etc/"
58 static const UChar SYSTEMV_SLASH
[] = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F, 0 }; // "SystemV/
59 static const UChar RIYADH8
[] = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38, 0 }; // "Riyadh8"
61 static UBool
contains(const char** list
, const char* str
) {
62 for (int32_t i
= 0; list
[i
]; i
++) {
63 if (uprv_strcmp(list
[i
], str
) == 0) {
71 TimeZoneFormatTest::runIndexedTest( int32_t index
, UBool exec
, const char* &name
, char* /*par*/ )
74 logln("TestSuite TimeZoneFormatTest");
77 TESTCASE(0, TestTimeZoneRoundTrip
);
78 TESTCASE(1, TestTimeRoundTrip
);
79 TESTCASE(2, TestParse
);
80 TESTCASE(3, TestISOFormat
);
81 TESTCASE(4, TestFormat
);
82 TESTCASE(5, TestFormatTZDBNames
);
83 default: name
= ""; break;
88 TimeZoneFormatTest::TestTimeZoneRoundTrip(void) {
89 UErrorCode status
= U_ZERO_ERROR
;
91 SimpleTimeZone
unknownZone(-31415, ETC_UNKNOWN
);
92 int32_t badDstOffset
= -1234;
93 int32_t badZoneOffset
= -2345;
95 int32_t testDateData
[][3] = {
104 Calendar
*cal
= Calendar::createInstance(TimeZone::createTimeZone((UnicodeString
)"UTC"), status
);
105 if (U_FAILURE(status
)) {
106 dataerrln("Calendar::createInstance failed: %s", u_errorName(status
));
110 // Set up rule equivalency test range
112 cal
->set(1900, UCAL_JANUARY
, 1);
113 low
= cal
->getTime(status
);
114 cal
->set(2040, UCAL_JANUARY
, 1);
115 high
= cal
->getTime(status
);
116 if (U_FAILURE(status
)) {
117 errln("getTime failed");
122 UDate DATES
[UPRV_LENGTHOF(testDateData
)];
123 const int32_t nDates
= UPRV_LENGTHOF(testDateData
);
125 for (int32_t i
= 0; i
< nDates
; i
++) {
126 cal
->set(testDateData
[i
][0], testDateData
[i
][1], testDateData
[i
][2]);
127 DATES
[i
] = cal
->getTime(status
);
128 if (U_FAILURE(status
)) {
129 errln("getTime failed");
134 // Set up test locales
135 const Locale testLocales
[] = {
142 const Locale
*LOCALES
;
146 LOCALES
= testLocales
;
147 nLocales
= UPRV_LENGTHOF(testLocales
);
149 LOCALES
= Locale::getAvailableLocales(nLocales
);
152 StringEnumeration
*tzids
= TimeZone::createEnumeration();
153 int32_t inRaw
, inDst
;
154 int32_t outRaw
, outDst
;
156 // Run the roundtrip test
157 for (int32_t locidx
= 0; locidx
< nLocales
; locidx
++) {
158 UnicodeString localGMTString
;
159 SimpleDateFormat
gmtFmt(UnicodeString("ZZZZ"), LOCALES
[locidx
], status
);
160 if (U_FAILURE(status
)) {
161 dataerrln("Error creating SimpleDateFormat - %s", u_errorName(status
));
164 gmtFmt
.setTimeZone(*TimeZone::getGMT());
165 gmtFmt
.format(0.0, localGMTString
);
167 for (int32_t patidx
= 0; patidx
< UPRV_LENGTHOF(PATTERNS
); patidx
++) {
169 SimpleDateFormat
*sdf
= new SimpleDateFormat((UnicodeString
)PATTERNS
[patidx
], LOCALES
[locidx
], status
);
170 if (U_FAILURE(status
)) {
171 dataerrln((UnicodeString
)"new SimpleDateFormat failed for pattern " +
172 PATTERNS
[patidx
] + " for locale " + LOCALES
[locidx
].getName() + " - " + u_errorName(status
));
173 status
= U_ZERO_ERROR
;
177 tzids
->reset(status
);
178 const UnicodeString
*tzid
;
179 while ((tzid
= tzids
->snext(status
))) {
180 TimeZone
*tz
= TimeZone::createTimeZone(*tzid
);
182 for (int32_t datidx
= 0; datidx
< nDates
; datidx
++) {
184 FieldPosition
fpos(0);
186 sdf
->setTimeZone(*tz
);
187 sdf
->format(DATES
[datidx
], tzstr
, fpos
);
189 // Before parse, set unknown zone to SimpleDateFormat instance
190 // just for making sure that it does not depends on the time zone
192 sdf
->setTimeZone(unknownZone
);
195 ParsePosition
pos(0);
196 Calendar
*outcal
= Calendar::createInstance(unknownZone
, status
);
197 if (U_FAILURE(status
)) {
198 errln("Failed to create an instance of calendar for receiving parse result.");
199 status
= U_ZERO_ERROR
;
202 outcal
->set(UCAL_DST_OFFSET
, badDstOffset
);
203 outcal
->set(UCAL_ZONE_OFFSET
, badZoneOffset
);
205 sdf
->parse(tzstr
, *outcal
, pos
);
208 const TimeZone
&outtz
= outcal
->getTimeZone();
209 UnicodeString outtzid
;
210 outtz
.getID(outtzid
);
212 tz
->getOffset(DATES
[datidx
], false, inRaw
, inDst
, status
);
213 if (U_FAILURE(status
)) {
214 errln((UnicodeString
)"Failed to get offsets from time zone" + *tzid
);
215 status
= U_ZERO_ERROR
;
217 outtz
.getOffset(DATES
[datidx
], false, outRaw
, outDst
, status
);
218 if (U_FAILURE(status
)) {
219 errln((UnicodeString
)"Failed to get offsets from time zone" + outtzid
);
220 status
= U_ZERO_ERROR
;
223 if (uprv_strcmp(PATTERNS
[patidx
], "V") == 0) {
224 // Short zone ID - should support roundtrip for canonical CLDR IDs
225 UnicodeString canonicalID
;
226 TimeZone::getCanonicalID(*tzid
, canonicalID
, status
);
227 if (U_FAILURE(status
)) {
228 // Uknown ID - we should not get here
229 errln((UnicodeString
)"Unknown ID " + *tzid
);
230 status
= U_ZERO_ERROR
;
231 } else if (outtzid
!= canonicalID
) {
232 if (outtzid
.compare(ETC_UNKNOWN
, -1) == 0) {
233 // Note that some zones like Asia/Riyadh87 does not have
234 // short zone ID and "unk" is used as fallback
235 logln((UnicodeString
)"Canonical round trip failed (probably as expected); tz=" + *tzid
236 + ", locale=" + LOCALES
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
237 + ", time=" + DATES
[datidx
] + ", str=" + tzstr
238 + ", outtz=" + outtzid
);
240 errln((UnicodeString
)"Canonical round trip failed; tz=" + *tzid
241 + ", locale=" + LOCALES
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
242 + ", time=" + DATES
[datidx
] + ", str=" + tzstr
243 + ", outtz=" + outtzid
);
246 } else if (uprv_strcmp(PATTERNS
[patidx
], "VV") == 0) {
247 // Zone ID - full roundtrip support
248 if (outtzid
!= *tzid
) {
249 errln((UnicodeString
)"Zone ID round trip failued; tz=" + *tzid
250 + ", locale=" + LOCALES
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
251 + ", time=" + DATES
[datidx
] + ", str=" + tzstr
252 + ", outtz=" + outtzid
);
254 } else if (uprv_strcmp(PATTERNS
[patidx
], "VVV") == 0 || uprv_strcmp(PATTERNS
[patidx
], "VVVV") == 0) {
255 // Location: time zone rule must be preserved except
256 // zones not actually associated with a specific location.
257 // Time zones in this category do not have "/" in its ID.
258 UnicodeString canonical
;
259 TimeZone::getCanonicalID(*tzid
, canonical
, status
);
260 if (U_FAILURE(status
)) {
261 // Uknown ID - we should not get here
262 errln((UnicodeString
)"Unknown ID " + *tzid
);
263 status
= U_ZERO_ERROR
;
264 } else if (outtzid
!= canonical
) {
265 // Canonical ID did not match - check the rules
266 if (!((BasicTimeZone
*)&outtz
)->hasEquivalentTransitions((BasicTimeZone
&)*tz
, low
, high
, TRUE
, status
)) {
267 if (canonical
.indexOf((UChar
)0x27 /*'/'*/) == -1) {
268 // Exceptional cases, such as CET, EET, MET and WET
269 logln((UnicodeString
)"Canonical round trip failed (as expected); tz=" + *tzid
270 + ", locale=" + LOCALES
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
271 + ", time=" + DATES
[datidx
] + ", str=" + tzstr
272 + ", outtz=" + outtzid
);
274 errln((UnicodeString
)"Canonical round trip failed; tz=" + *tzid
275 + ", locale=" + LOCALES
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
276 + ", time=" + DATES
[datidx
] + ", str=" + tzstr
277 + ", outtz=" + outtzid
);
279 if (U_FAILURE(status
)) {
280 errln("hasEquivalentTransitions failed");
281 status
= U_ZERO_ERROR
;
287 UBool isOffsetFormat
= (*PATTERNS
[patidx
] == 'Z'
288 || *PATTERNS
[patidx
] == 'O'
289 || *PATTERNS
[patidx
] == 'X'
290 || *PATTERNS
[patidx
] == 'x');
291 UBool minutesOffset
= FALSE
;
292 if (*PATTERNS
[patidx
] == 'X' || *PATTERNS
[patidx
] == 'x') {
293 minutesOffset
= (uprv_strlen(PATTERNS
[patidx
]) <= 3);
296 if (!isOffsetFormat
) {
297 // Check if localized GMT format is used as a fallback of name styles
298 int32_t numDigits
= 0;
299 for (int n
= 0; n
< tzstr
.length(); n
++) {
300 if (u_isdigit(tzstr
.charAt(n
))) {
304 isOffsetFormat
= (numDigits
> 0);
306 if (isOffsetFormat
|| tzstr
== localGMTString
) {
307 // Localized GMT or ISO: total offset (raw + dst) must be preserved.
308 int32_t inOffset
= inRaw
+ inDst
;
309 int32_t outOffset
= outRaw
+ outDst
;
310 int32_t diff
= outOffset
- inOffset
;
312 diff
= (diff
/ 60000) * 60000;
315 errln((UnicodeString
)"Offset round trip failed; tz=" + *tzid
316 + ", locale=" + LOCALES
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
317 + ", time=" + DATES
[datidx
] + ", str=" + tzstr
318 + ", inOffset=" + inOffset
+ ", outOffset=" + outOffset
);
321 // Specific or generic: raw offset must be preserved.
322 if (inRaw
!= outRaw
) {
323 errln((UnicodeString
)"Raw offset round trip failed; tz=" + *tzid
324 + ", locale=" + LOCALES
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
325 + ", time=" + DATES
[datidx
] + ", str=" + tzstr
326 + ", inRawOffset=" + inRaw
+ ", outRawOffset=" + outRaw
);
341 // Special exclusions in TestTimeZoneRoundTrip.
342 // These special cases do not round trip time as designed.
343 static UBool
isSpecialTimeRoundTripCase(const char* loc
,
344 const UnicodeString
& id
,
353 {NULL
, "Asia/Chita", "zzzz", 1414252800000.0},
354 {NULL
, "Asia/Chita", "vvvv", 1414252800000.0},
355 {NULL
, "Asia/Srednekolymsk", "zzzz", 1414241999999.0},
356 {NULL
, "Asia/Srednekolymsk", "vvvv", 1414241999999.0},
357 {NULL
, NULL
, NULL
, U_DATE_MIN
}
360 UBool isExcluded
= FALSE
;
361 for (int32_t i
= 0; EXCLUSIONS
[i
].id
!= NULL
; i
++) {
362 if (EXCLUSIONS
[i
].loc
== NULL
|| uprv_strcmp(loc
, EXCLUSIONS
[i
].loc
) == 0) {
363 if (id
.compare(EXCLUSIONS
[i
].id
) == 0) {
364 if (EXCLUSIONS
[i
].pattern
== NULL
|| uprv_strcmp(pattern
, EXCLUSIONS
[i
].pattern
) == 0) {
365 if (EXCLUSIONS
[i
].time
== U_DATE_MIN
|| EXCLUSIONS
[i
].time
== time
) {
375 // LocaleData. Somewhat misnamed. For TestTimeZoneRoundTrip, specifies the locales and patterns
376 // to be tested, and provides an iterator over these for the multi-threaded test
377 // functions to pick up the next combination to be tested.
379 // A single global instance of this struct is shared among all
382 // "locales" is an array of locales to be tested.
383 // PATTERNS (a global) is an array of patterns to be tested for each locale.
384 // "localeIndex" and "patternIndex" keep track of the iteration through the above.
385 // Each of the parallel test threads calls LocaleData::nextTest() in a loop
386 // to find out what to test next. It must be thread safe.
389 int32_t patternIndex
;
391 UDate times
[UPRV_LENGTHOF(PATTERNS
)]; // Performance data, Elapsed time for each pattern.
392 const Locale
* locales
;
398 LocaleData() : localeIndex(0), patternIndex(0), testCounts(0), locales(NULL
),
399 nLocales(0), START_TIME(0), END_TIME(0), numDone(0) {
400 for (int i
=0; i
<UPRV_LENGTHOF(times
); i
++) {
405 void resetTestIteration() {
407 patternIndex
= UPRV_LENGTHOF(PATTERNS
);
411 UBool
nextTest(int &rLocaleIndex
, int &rPatternIndex
) {
413 if (patternIndex
>= UPRV_LENGTHOF(PATTERNS
) - 1) {
414 if (localeIndex
>= nLocales
- 1) {
421 rLocaleIndex
= localeIndex
;
422 rPatternIndex
= patternIndex
;
427 void addTime(UDate amount
, int32_t patIdx
) {
429 U_ASSERT(patIdx
< UPRV_LENGTHOF(PATTERNS
));
430 times
[patIdx
] += amount
;
434 static LocaleData
*gLocaleData
= NULL
;
437 TimeZoneFormatTest::TestTimeRoundTrip(void) {
438 UErrorCode status
= U_ZERO_ERROR
;
439 LocalPointer
<Calendar
> cal(Calendar::createInstance(TimeZone::createTimeZone((UnicodeString
) "UTC"), status
));
440 if (U_FAILURE(status
)) {
441 dataerrln("Calendar::createInstance failed: %s", u_errorName(status
));
445 const char* testAllProp
= getProperty("TimeZoneRoundTripAll");
446 UBool bTestAll
= (testAllProp
&& uprv_strcmp(testAllProp
, "true") == 0);
448 UDate START_TIME
, END_TIME
;
449 if (bTestAll
|| !quick
) {
450 cal
->set(1900, UCAL_JANUARY
, 1);
452 cal
->set(1999, UCAL_JANUARY
, 1);
454 START_TIME
= cal
->getTime(status
);
456 cal
->set(2022, UCAL_JANUARY
, 1);
457 END_TIME
= cal
->getTime(status
);
459 if (U_FAILURE(status
)) {
460 errln("getTime failed");
464 LocaleData localeData
;
465 gLocaleData
= &localeData
;
467 // Set up test locales
468 const Locale locales1
[] = {Locale("en")};
469 const Locale locales2
[] = {
470 Locale("ar_EG"), Locale("bg_BG"), Locale("ca_ES"), Locale("da_DK"), Locale("de"),
471 Locale("de_DE"), Locale("el_GR"), Locale("en"), Locale("en_AU"), Locale("en_CA"),
472 Locale("en_US"), Locale("es"), Locale("es_ES"), Locale("es_MX"), Locale("fi_FI"),
473 Locale("fr"), Locale("fr_CA"), Locale("fr_FR"), Locale("he_IL"), Locale("hu_HU"),
474 Locale("it"), Locale("it_IT"), Locale("ja"), Locale("ja_JP"), Locale("ko"),
475 Locale("ko_KR"), Locale("nb_NO"), Locale("nl_NL"), Locale("nn_NO"), Locale("pl_PL"),
476 Locale("pt"), Locale("pt_BR"), Locale("pt_PT"), Locale("ru_RU"), Locale("sv_SE"),
477 Locale("th_TH"), Locale("tr_TR"), Locale("zh"), Locale("zh_Hans"), Locale("zh_Hans_CN"),
478 Locale("zh_Hant"), Locale("zh_Hant_TW")
482 gLocaleData
->locales
= Locale::getAvailableLocales(gLocaleData
->nLocales
);
484 gLocaleData
->locales
= locales1
;
485 gLocaleData
->nLocales
= UPRV_LENGTHOF(locales1
);
487 gLocaleData
->locales
= locales2
;
488 gLocaleData
->nLocales
= UPRV_LENGTHOF(locales2
);
491 gLocaleData
->START_TIME
= START_TIME
;
492 gLocaleData
->END_TIME
= END_TIME
;
493 gLocaleData
->resetTestIteration();
495 // start IntlTest.threadCount threads, each running the function RunTimeRoundTripTests().
497 ThreadPool
<TimeZoneFormatTest
> threads(this, threadCount
, &TimeZoneFormatTest::RunTimeRoundTripTests
);
498 threads
.start(); // Start all threads.
499 threads
.join(); // Wait for all threads to finish.
502 logln("### Elapsed time by patterns ###");
503 for (int32_t i
= 0; i
< UPRV_LENGTHOF(PATTERNS
); i
++) {
504 logln(UnicodeString("") + gLocaleData
->times
[i
] + "ms (" + PATTERNS
[i
] + ")");
505 total
+= gLocaleData
->times
[i
];
507 logln((UnicodeString
) "Total: " + total
+ "ms");
508 logln((UnicodeString
) "Iteration: " + gLocaleData
->testCounts
);
512 // TimeZoneFormatTest::RunTimeRoundTripTests()
513 // This function loops, running time zone format round trip test cases until there are no more, then returns.
514 // Threading: multiple invocations of this function are started in parallel
515 // by TimeZoneFormatTest::TestTimeRoundTrip()
517 void TimeZoneFormatTest::RunTimeRoundTripTests(int32_t threadNumber
) {
518 UErrorCode status
= U_ZERO_ERROR
;
519 UBool REALLY_VERBOSE
= FALSE
;
521 // These patterns are ambiguous at DST->STD local time overlap
522 const char* AMBIGUOUS_DST_DECESSION
[] = { "v", "vvvv", "V", "VV", "VVV", "VVVV", 0 };
524 // These patterns are ambiguous at STD->STD/DST->DST local time overlap
525 const char* AMBIGUOUS_NEGATIVE_SHIFT
[] = { "z", "zzzz", "v", "vvvv", "V", "VV", "VVV", "VVVV", 0 };
527 // These patterns only support integer minutes offset
528 const char* MINUTES_OFFSET
[] = { "X", "XX", "XXX", "x", "xx", "xxx", 0 };
530 // Workaround for #6338
531 //UnicodeString BASEPATTERN("yyyy-MM-dd'T'HH:mm:ss.SSS");
532 UnicodeString
BASEPATTERN("yyyy.MM.dd HH:mm:ss.SSS");
534 // timer for performance analysis
537 UBool expectedRoundTrip
[4];
540 StringEnumeration
*tzids
= TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL
, NULL
, NULL
, status
);
541 if (U_FAILURE(status
)) {
542 if (status
== U_MISSING_RESOURCE_ERROR
) {
543 // This error is generally caused by data not being present.
544 dataerrln("TimeZone::createTimeZoneIDEnumeration failed - %s", u_errorName(status
));
546 errln("TimeZone::createTimeZoneIDEnumeration failed: %s", u_errorName(status
));
554 while (gLocaleData
->nextTest(locidx
, patidx
)) {
556 UnicodeString
pattern(BASEPATTERN
);
557 pattern
.append(" ").append(PATTERNS
[patidx
]);
558 logln(" Thread %d, Locale %s, Pattern %s",
559 threadNumber
, gLocaleData
->locales
[locidx
].getName(), CStr(pattern
)());
561 SimpleDateFormat
*sdf
= new SimpleDateFormat(pattern
, gLocaleData
->locales
[locidx
], status
);
562 if (U_FAILURE(status
)) {
563 errcheckln(status
, (UnicodeString
) "new SimpleDateFormat failed for pattern " +
564 pattern
+ " for locale " + gLocaleData
->locales
[locidx
].getName() + " - " + u_errorName(status
));
565 status
= U_ZERO_ERROR
;
569 UBool minutesOffset
= contains(MINUTES_OFFSET
, PATTERNS
[patidx
]);
571 tzids
->reset(status
);
572 const UnicodeString
*tzid
;
574 timer
= Calendar::getNow();
576 while ((tzid
= tzids
->snext(status
))) {
577 if (uprv_strcmp(PATTERNS
[patidx
], "V") == 0) {
578 // Some zones do not have short ID assigned, such as Asia/Riyadh87.
579 // The time roundtrip will fail for such zones with pattern "V" (short zone ID).
580 // This is expected behavior.
581 const UChar
* shortZoneID
= ZoneMeta::getShortID(*tzid
);
582 if (shortZoneID
== NULL
) {
585 } else if (uprv_strcmp(PATTERNS
[patidx
], "VVV") == 0) {
586 // Some zones are not associated with any region, such as Etc/GMT+8.
587 // The time roundtrip will fail for such zone with pattern "VVV" (exemplar location).
588 // This is expected behavior.
589 if (tzid
->indexOf((UChar
)0x2F) < 0 || tzid
->indexOf(ETC_SLASH
, -1, 0) >= 0
590 || tzid
->indexOf(SYSTEMV_SLASH
, -1, 0) >= 0 || tzid
->indexOf(RIYADH8
, -1, 0) >= 0) {
595 if (*tzid
== "Pacific/Apia" && uprv_strcmp(PATTERNS
[patidx
], "vvvv") == 0
596 && logKnownIssue("11052", "Ambiguous zone name - Samoa Time")) {
599 // skip some zone/format combinations until we have a metazone for them and/or
600 // better fallback zone formatting...
601 if ((*tzid
== "America/Punta_Arenas" || *tzid
== "Antarctica/Palmer") &&
602 uprv_strcmp(PATTERNS
[patidx
], "zzzz") == 0) {
606 BasicTimeZone
*tz
= (BasicTimeZone
*) TimeZone::createTimeZone(*tzid
);
607 sdf
->setTimeZone(*tz
);
609 UDate t
= gLocaleData
->START_TIME
;
610 TimeZoneTransition tzt
;
611 UBool tztAvail
= FALSE
;
614 while (t
< gLocaleData
->END_TIME
) {
617 expectedRoundTrip
[0] = TRUE
;
620 int32_t fromOffset
= tzt
.getFrom()->getRawOffset() + tzt
.getFrom()->getDSTSavings();
621 int32_t toOffset
= tzt
.getTo()->getRawOffset() + tzt
.getTo()->getDSTSavings();
622 int32_t delta
= toOffset
- fromOffset
;
624 UBool isDstDecession
= tzt
.getFrom()->getDSTSavings() > 0 && tzt
.getTo()->getDSTSavings() == 0;
625 testTimes
[0] = t
+ delta
- 1;
626 expectedRoundTrip
[0] = TRUE
;
627 testTimes
[1] = t
+ delta
;
628 expectedRoundTrip
[1] = isDstDecession
?
629 !contains(AMBIGUOUS_DST_DECESSION
, PATTERNS
[patidx
]) :
630 !contains(AMBIGUOUS_NEGATIVE_SHIFT
, PATTERNS
[patidx
]);
631 testTimes
[2] = t
- 1;
632 expectedRoundTrip
[2] = isDstDecession
?
633 !contains(AMBIGUOUS_DST_DECESSION
, PATTERNS
[patidx
]) :
634 !contains(AMBIGUOUS_NEGATIVE_SHIFT
, PATTERNS
[patidx
]);
636 expectedRoundTrip
[3] = TRUE
;
639 testTimes
[0] = t
- 1;
640 expectedRoundTrip
[0] = TRUE
;
642 expectedRoundTrip
[1] = TRUE
;
646 for (int32_t testidx
= 0; testidx
< testLen
; testidx
++) {
648 // reduce regular test time
649 if (!expectedRoundTrip
[testidx
]) {
656 gLocaleData
->testCounts
++;
660 FieldPosition
fpos(0);
661 sdf
->format(testTimes
[testidx
], text
, fpos
);
663 UDate parsedDate
= sdf
->parse(text
, status
);
664 if (U_FAILURE(status
)) {
665 errln((UnicodeString
) "Parse failure for text=" + text
+ ", tzid=" + *tzid
+ ", locale=" + gLocaleData
->locales
[locidx
].getName()
666 + ", pattern=" + PATTERNS
[patidx
] + ", time=" + testTimes
[testidx
]);
667 status
= U_ZERO_ERROR
;
671 int32_t timeDiff
= (int32_t)(parsedDate
- testTimes
[testidx
]);
672 UBool bTimeMatch
= minutesOffset
?
673 (timeDiff
/60000)*60000 == 0 : timeDiff
== 0;
675 UnicodeString msg
= (UnicodeString
) "Time round trip failed for " + "tzid=" + *tzid
676 + ", locale=" + gLocaleData
->locales
[locidx
].getName() + ", pattern=" + PATTERNS
[patidx
]
677 + ", text=" + text
+ ", time=" + testTimes
[testidx
] + ", restime=" + parsedDate
+ ", diff=" + (parsedDate
- testTimes
[testidx
]);
678 // Timebomb for TZData update
679 if (expectedRoundTrip
[testidx
]
680 && !isSpecialTimeRoundTripCase(gLocaleData
->locales
[locidx
].getName(), *tzid
,
681 PATTERNS
[patidx
], testTimes
[testidx
])) {
682 errln((UnicodeString
) "FAIL: " + msg
);
683 } else if (REALLY_VERBOSE
) {
688 tztAvail
= tz
->getNextTransition(t
, FALSE
, tzt
);
693 // Test the date in the middle of two transitions.
694 t
+= (int64_t) ((tzt
.getTime() - t
) / 2);
703 UDate elapsedTime
= Calendar::getNow() - timer
;
704 gLocaleData
->addTime(elapsedTime
, patidx
);
715 UTimeZoneFormatStyle style
;
716 uint32_t parseOptions
;
717 const char* expected
;
719 UTimeZoneFormatTimeType timeType
;
723 TimeZoneFormatTest::TestParse(void) {
724 const ParseTestData DATA
[] = {
725 // text inPos locale style
726 // parseOptions expected outPos timeType
727 {"Z", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL
,
728 UTZFMT_PARSE_OPTION_NONE
, "Etc/GMT", 1, UTZFMT_TIME_TYPE_UNKNOWN
},
730 {"Z", 0, "en_US", UTZFMT_STYLE_SPECIFIC_LONG
,
731 UTZFMT_PARSE_OPTION_NONE
, "Etc/GMT", 1, UTZFMT_TIME_TYPE_UNKNOWN
},
733 {"Zambia time", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL
,
734 UTZFMT_PARSE_OPTION_ALL_STYLES
, "Etc/GMT", 1, UTZFMT_TIME_TYPE_UNKNOWN
},
736 {"Zambia time", 0, "en_US", UTZFMT_STYLE_GENERIC_LOCATION
,
737 UTZFMT_PARSE_OPTION_NONE
, "Africa/Lusaka", 11, UTZFMT_TIME_TYPE_UNKNOWN
},
739 {"Zambia time", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
,
740 UTZFMT_PARSE_OPTION_ALL_STYLES
, "Africa/Lusaka", 11, UTZFMT_TIME_TYPE_UNKNOWN
},
742 {"+00:00", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL
,
743 UTZFMT_PARSE_OPTION_NONE
, "Etc/GMT", 6, UTZFMT_TIME_TYPE_UNKNOWN
},
745 {"-01:30:45", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL
,
746 UTZFMT_PARSE_OPTION_NONE
, "GMT-01:30:45", 9, UTZFMT_TIME_TYPE_UNKNOWN
},
748 {"-7", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
,
749 UTZFMT_PARSE_OPTION_NONE
, "GMT-07:00", 2, UTZFMT_TIME_TYPE_UNKNOWN
},
751 {"-2222", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
,
752 UTZFMT_PARSE_OPTION_NONE
, "GMT-22:22", 5, UTZFMT_TIME_TYPE_UNKNOWN
},
754 {"-3333", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
,
755 UTZFMT_PARSE_OPTION_NONE
, "GMT-03:33", 4, UTZFMT_TIME_TYPE_UNKNOWN
},
757 {"XXX+01:30YYY", 3, "en_US", UTZFMT_STYLE_LOCALIZED_GMT
,
758 UTZFMT_PARSE_OPTION_NONE
, "GMT+01:30", 9, UTZFMT_TIME_TYPE_UNKNOWN
},
760 {"GMT0", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT
,
761 UTZFMT_PARSE_OPTION_NONE
, "Etc/GMT", 3, UTZFMT_TIME_TYPE_UNKNOWN
},
763 {"EST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT
,
764 UTZFMT_PARSE_OPTION_NONE
, "America/New_York", 3, UTZFMT_TIME_TYPE_STANDARD
},
766 {"ESTx", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT
,
767 UTZFMT_PARSE_OPTION_NONE
, "America/New_York", 3, UTZFMT_TIME_TYPE_STANDARD
},
769 {"EDTx", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT
,
770 UTZFMT_PARSE_OPTION_NONE
, "America/New_York", 3, UTZFMT_TIME_TYPE_DAYLIGHT
},
772 {"EST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_LONG
,
773 UTZFMT_PARSE_OPTION_NONE
, NULL
, 0, UTZFMT_TIME_TYPE_UNKNOWN
},
775 {"EST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_LONG
,
776 UTZFMT_PARSE_OPTION_ALL_STYLES
, "America/New_York", 3, UTZFMT_TIME_TYPE_STANDARD
},
778 {"EST", 0, "en_CA", UTZFMT_STYLE_SPECIFIC_SHORT
,
779 UTZFMT_PARSE_OPTION_NONE
, "America/Toronto", 3, UTZFMT_TIME_TYPE_STANDARD
},
781 {"CST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT
,
782 UTZFMT_PARSE_OPTION_NONE
, "America/Chicago", 3, UTZFMT_TIME_TYPE_STANDARD
},
784 {"CST", 0, "en_GB", UTZFMT_STYLE_SPECIFIC_SHORT
,
785 UTZFMT_PARSE_OPTION_NONE
, NULL
, 0, UTZFMT_TIME_TYPE_UNKNOWN
},
787 {"CST", 0, "en_GB", UTZFMT_STYLE_SPECIFIC_SHORT
,
788 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS
, "America/Chicago", 3, UTZFMT_TIME_TYPE_STANDARD
},
790 {"--CST--", 2, "en_GB", UTZFMT_STYLE_SPECIFIC_SHORT
,
791 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS
, "America/Chicago", 5, UTZFMT_TIME_TYPE_STANDARD
},
793 {"CST", 0, "zh_CN", UTZFMT_STYLE_SPECIFIC_SHORT
,
794 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS
, "Asia/Shanghai", 3, UTZFMT_TIME_TYPE_STANDARD
},
796 {"AEST", 0, "en_AU", UTZFMT_STYLE_SPECIFIC_SHORT
,
797 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS
, "Australia/Sydney", 4, UTZFMT_TIME_TYPE_STANDARD
},
799 {"AST", 0, "ar_SA", UTZFMT_STYLE_SPECIFIC_SHORT
,
800 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS
, "Asia/Riyadh", 3, UTZFMT_TIME_TYPE_STANDARD
},
802 {"AQTST", 0, "en", UTZFMT_STYLE_SPECIFIC_LONG
,
803 UTZFMT_PARSE_OPTION_NONE
, NULL
, 0, UTZFMT_TIME_TYPE_UNKNOWN
},
805 {"AQTST", 0, "en", UTZFMT_STYLE_SPECIFIC_LONG
,
806 UTZFMT_PARSE_OPTION_ALL_STYLES
, NULL
, 0, UTZFMT_TIME_TYPE_UNKNOWN
},
808 {"AQTST", 0, "en", UTZFMT_STYLE_SPECIFIC_LONG
,
809 UTZFMT_PARSE_OPTION_ALL_STYLES
| UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS
, "Asia/Aqtobe", 5, UTZFMT_TIME_TYPE_DAYLIGHT
},
811 {NULL
, 0, NULL
, UTZFMT_STYLE_GENERIC_LOCATION
,
812 UTZFMT_PARSE_OPTION_NONE
, NULL
, 0, UTZFMT_TIME_TYPE_UNKNOWN
}
815 for (int32_t i
= 0; DATA
[i
].text
; i
++) {
816 UErrorCode status
= U_ZERO_ERROR
;
817 LocalPointer
<TimeZoneFormat
> tzfmt(TimeZoneFormat::createInstance(Locale(DATA
[i
].locale
), status
));
818 if (U_FAILURE(status
)) {
819 dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status
));
822 UTimeZoneFormatTimeType ttype
= UTZFMT_TIME_TYPE_UNKNOWN
;
823 ParsePosition
pos(DATA
[i
].inPos
);
824 TimeZone
* tz
= tzfmt
->parse(DATA
[i
].style
, DATA
[i
].text
, pos
, DATA
[i
].parseOptions
, &ttype
);
826 UnicodeString errMsg
;
830 if (outID
!= UnicodeString(DATA
[i
].expected
)) {
831 errMsg
= (UnicodeString
)"Time zone ID: " + outID
+ " - expected: " + DATA
[i
].expected
;
832 } else if (pos
.getIndex() != DATA
[i
].outPos
) {
833 errMsg
= (UnicodeString
)"Parsed pos: " + pos
.getIndex() + " - expected: " + DATA
[i
].outPos
;
834 } else if (ttype
!= DATA
[i
].timeType
) {
835 errMsg
= (UnicodeString
)"Time type: " + ttype
+ " - expected: " + DATA
[i
].timeType
;
839 if (DATA
[i
].expected
) {
840 errln((UnicodeString
)"Fail: Parse failure - expected: " + DATA
[i
].expected
);
843 if (errMsg
.length() > 0) {
844 errln((UnicodeString
)"Fail: " + errMsg
+ " [text=" + DATA
[i
].text
+ ", pos=" + DATA
[i
].inPos
+ ", style=" + DATA
[i
].style
+ "]");
850 TimeZoneFormatTest::TestISOFormat(void) {
851 const int32_t OFFSET
[] = {
856 -77777, // -1m 17.777s
860 -37800000, // -10h 30m
861 -37845000, // -10h 30m 45s
865 const char* ISO_STR
[][11] = {
868 "Z", "Z", "Z", "Z", "Z",
869 "+00", "+0000", "+00:00", "+0000", "+00:00",
874 "Z", "Z", "Z", "Z", "Z",
875 "+00", "+0000", "+00:00", "+0000", "+00:00",
880 "Z", "Z", "Z", "-000059", "-00:00:59",
881 "+00", "+0000", "+00:00", "-000059", "-00:00:59",
886 "+0001", "+0001", "+00:01", "+0001", "+00:01",
887 "+0001", "+0001", "+00:01", "+0001", "+00:01",
892 "-0001", "-0001", "-00:01", "-000117", "-00:01:17",
893 "-0001", "-0001", "-00:01", "-000117", "-00:01:17",
898 "+0030", "+0030", "+00:30", "+0030", "+00:30",
899 "+0030", "+0030", "+00:30", "+0030", "+00:30",
904 "-01", "-0100", "-01:00", "-0100", "-01:00",
905 "-01", "-0100", "-01:00", "-0100", "-01:00",
910 "+10", "+1000", "+10:00", "+1000", "+10:00",
911 "+10", "+1000", "+10:00", "+1000", "+10:00",
916 "-1030", "-1030", "-10:30", "-1030", "-10:30",
917 "-1030", "-1030", "-10:30", "-1030", "-10:30",
922 "-1030", "-1030", "-10:30", "-103045", "-10:30:45",
923 "-1030", "-1030", "-10:30", "-103045", "-10:30:45",
934 const char* PATTERN
[] = {
935 "X", "XX", "XXX", "XXXX", "XXXXX",
936 "x", "xx", "xxx", "xxxx", "xxxxx",
937 "Z", // equivalent to "xxxx"
941 const int32_t MIN_OFFSET_UNIT
[] = {
942 60000, 60000, 60000, 1000, 1000,
943 60000, 60000, 60000, 1000, 1000,
948 UErrorCode status
= U_ZERO_ERROR
;
949 LocalPointer
<SimpleDateFormat
> sdf(new SimpleDateFormat(status
), status
);
950 if (U_FAILURE(status
)) {
951 dataerrln("Fail new SimpleDateFormat: %s", u_errorName(status
));
954 UDate d
= Calendar::getNow();
956 for (uint32_t i
= 0; i
< UPRV_LENGTHOF(OFFSET
); i
++) {
957 SimpleTimeZone
* tz
= new SimpleTimeZone(OFFSET
[i
], UnicodeString("Zone Offset:") + OFFSET
[i
] + "ms");
958 sdf
->adoptTimeZone(tz
);
959 for (int32_t j
= 0; PATTERN
[j
] != 0; j
++) {
960 sdf
->applyPattern(UnicodeString(PATTERN
[j
]));
961 UnicodeString result
;
962 sdf
->format(d
, result
);
965 if (result
!= UnicodeString(ISO_STR
[i
][j
])) {
966 errln((UnicodeString
)"FAIL: pattern=" + PATTERN
[j
] + ", offset=" + OFFSET
[i
] + " -> "
967 + result
+ " (expected: " + ISO_STR
[i
][j
] + ")");
970 // Offset out of range
971 // Note: for now, there is no way to propagate the error status through
972 // the SimpleDateFormat::format above.
973 if (result
.length() > 0) {
974 errln((UnicodeString
)"FAIL: Non-Empty result for pattern=" + PATTERN
[j
] + ", offset=" + OFFSET
[i
]
975 + " (expected: empty result)");
982 LocalPointer
<Calendar
> outcal(Calendar::createInstance(status
));
983 if (U_FAILURE(status
)) {
984 dataerrln("Fail new Calendar: %s", u_errorName(status
));
987 for (int32_t i
= 0; ISO_STR
[i
][0] != NULL
; i
++) {
988 for (int32_t j
= 0; PATTERN
[j
] != 0; j
++) {
989 if (ISO_STR
[i
][j
] == 0) {
992 ParsePosition
pos(0);
993 SimpleTimeZone
* bogusTZ
= new SimpleTimeZone(-1, UnicodeString("Zone Offset: -1ms"));
994 outcal
->adoptTimeZone(bogusTZ
);
995 sdf
->applyPattern(PATTERN
[j
]);
997 sdf
->parse(UnicodeString(ISO_STR
[i
][j
]), *(outcal
.getAlias()), pos
);
999 if (pos
.getIndex() != (int32_t)uprv_strlen(ISO_STR
[i
][j
])) {
1000 errln((UnicodeString
)"FAIL: Failed to parse the entire input string: " + ISO_STR
[i
][j
]);
1003 const TimeZone
& outtz
= outcal
->getTimeZone();
1004 int32_t outOffset
= outtz
.getRawOffset();
1005 int32_t adjustedOffset
= OFFSET
[i
] / MIN_OFFSET_UNIT
[j
] * MIN_OFFSET_UNIT
[j
];
1006 if (outOffset
!= adjustedOffset
) {
1007 errln((UnicodeString
)"FAIL: Incorrect offset:" + outOffset
+ "ms for input string: " + ISO_STR
[i
][j
]
1008 + " (expected:" + adjustedOffset
+ "ms)");
1019 UTimeZoneFormatStyle style
;
1020 const char* expected
;
1021 UTimeZoneFormatTimeType timeType
;
1025 TimeZoneFormatTest::TestFormat(void) {
1026 UDate dateJan
= 1358208000000.0; // 2013-01-15T00:00:00Z
1027 UDate dateJul
= 1373846400000.0; // 2013-07-15T00:00:00Z
1029 const FormatTestData DATA
[] = {
1032 "America/Los_Angeles",
1034 UTZFMT_STYLE_GENERIC_LOCATION
,
1036 UTZFMT_TIME_TYPE_UNKNOWN
1040 "America/Los_Angeles",
1042 UTZFMT_STYLE_GENERIC_LONG
,
1044 UTZFMT_TIME_TYPE_UNKNOWN
1048 "America/Los_Angeles",
1050 UTZFMT_STYLE_SPECIFIC_LONG
,
1051 "Pacific Standard Time",
1052 UTZFMT_TIME_TYPE_STANDARD
1056 "America/Los_Angeles",
1058 UTZFMT_STYLE_SPECIFIC_LONG
,
1059 "Pacific Daylight Time",
1060 UTZFMT_TIME_TYPE_DAYLIGHT
1064 "America/Los_Angeles",
1066 UTZFMT_STYLE_ZONE_ID
,
1067 "America/Los_Angeles",
1068 UTZFMT_TIME_TYPE_UNKNOWN
1072 "America/Los_Angeles",
1074 UTZFMT_STYLE_ZONE_ID_SHORT
,
1076 UTZFMT_TIME_TYPE_UNKNOWN
1080 "America/Los_Angeles",
1082 UTZFMT_STYLE_EXEMPLAR_LOCATION
,
1084 UTZFMT_TIME_TYPE_UNKNOWN
1091 UTZFMT_STYLE_GENERIC_LONG
,
1092 "\\u65E5\\u672C\\u6A19\\u6E96\\u6642",
1093 UTZFMT_TIME_TYPE_UNKNOWN
1096 {0, 0, 0.0, UTZFMT_STYLE_GENERIC_LOCATION
, 0, UTZFMT_TIME_TYPE_UNKNOWN
}
1099 for (int32_t i
= 0; DATA
[i
].locale
; i
++) {
1100 UErrorCode status
= U_ZERO_ERROR
;
1101 LocalPointer
<TimeZoneFormat
> tzfmt(TimeZoneFormat::createInstance(Locale(DATA
[i
].locale
), status
));
1102 if (U_FAILURE(status
)) {
1103 dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status
));
1107 LocalPointer
<TimeZone
> tz(TimeZone::createTimeZone(DATA
[i
].tzid
));
1109 UTimeZoneFormatTimeType timeType
;
1111 tzfmt
->format(DATA
[i
].style
, *(tz
.getAlias()), DATA
[i
].date
, out
, &timeType
);
1112 UnicodeString
expected(DATA
[i
].expected
, -1, US_INV
);
1113 expected
= expected
.unescape();
1115 assertEquals(UnicodeString("Format result for ") + DATA
[i
].tzid
+ " (Test Case " + i
+ ")", expected
, out
);
1116 if (DATA
[i
].timeType
!= timeType
) {
1117 dataerrln(UnicodeString("Formatted time zone type (Test Case ") + i
+ "), returned="
1118 + timeType
+ ", expected=" + DATA
[i
].timeType
);
1124 TimeZoneFormatTest::TestFormatTZDBNames(void) {
1125 UDate dateJan
= 1358208000000.0; // 2013-01-15T00:00:00Z
1126 UDate dateJul
= 1373846400000.0; // 2013-07-15T00:00:00Z
1128 const FormatTestData DATA
[] = {
1133 UTZFMT_STYLE_SPECIFIC_SHORT
,
1135 UTZFMT_TIME_TYPE_STANDARD
1141 UTZFMT_STYLE_SPECIFIC_SHORT
,
1143 UTZFMT_TIME_TYPE_STANDARD
1149 UTZFMT_STYLE_SPECIFIC_SHORT
,
1151 UTZFMT_TIME_TYPE_STANDARD
1155 "America/Los_Angeles",
1157 UTZFMT_STYLE_SPECIFIC_LONG
,
1158 "GMT-07:00", // No long display names
1159 UTZFMT_TIME_TYPE_DAYLIGHT
1163 "America/Los_Angeles",
1165 UTZFMT_STYLE_SPECIFIC_SHORT
,
1167 UTZFMT_TIME_TYPE_DAYLIGHT
1173 UTZFMT_STYLE_SPECIFIC_SHORT
,
1175 UTZFMT_TIME_TYPE_DAYLIGHT
1181 UTZFMT_STYLE_SPECIFIC_SHORT
,
1183 UTZFMT_TIME_TYPE_STANDARD
1186 {0, 0, 0.0, UTZFMT_STYLE_GENERIC_LOCATION
, 0, UTZFMT_TIME_TYPE_UNKNOWN
}
1189 for (int32_t i
= 0; DATA
[i
].locale
; i
++) {
1190 UErrorCode status
= U_ZERO_ERROR
;
1191 Locale
loc(DATA
[i
].locale
);
1192 LocalPointer
<TimeZoneFormat
> tzfmt(TimeZoneFormat::createInstance(loc
, status
));
1193 if (U_FAILURE(status
)) {
1194 dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status
));
1197 TimeZoneNames
*tzdbNames
= TimeZoneNames::createTZDBInstance(loc
, status
);
1198 if (U_FAILURE(status
)) {
1199 dataerrln("Fail TimeZoneNames::createTZDBInstance: %s", u_errorName(status
));
1202 tzfmt
->adoptTimeZoneNames(tzdbNames
);
1204 LocalPointer
<TimeZone
> tz(TimeZone::createTimeZone(DATA
[i
].tzid
));
1206 UTimeZoneFormatTimeType timeType
;
1208 tzfmt
->format(DATA
[i
].style
, *(tz
.getAlias()), DATA
[i
].date
, out
, &timeType
);
1209 UnicodeString
expected(DATA
[i
].expected
, -1, US_INV
);
1210 expected
= expected
.unescape();
1212 assertEquals(UnicodeString("Format result for ") + DATA
[i
].tzid
+ " (Test Case " + i
+ ")", expected
, out
);
1213 if (DATA
[i
].timeType
!= timeType
) {
1214 dataerrln(UnicodeString("Formatted time zone type (Test Case ") + i
+ "), returned="
1215 + timeType
+ ", expected=" + DATA
[i
].timeType
);
1221 #endif /* #if !UCONFIG_NO_FORMATTING */