1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /********************************************************************
4 * COPYRIGHT:
5 * Copyright (c) 2008-2016, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 ********************************************************************/
8
9 #include "unicode/utypes.h"
10
11 #if !UCONFIG_NO_FORMATTING
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include "dtptngts.h"
16
17 #include "unicode/calendar.h"
18 #include "unicode/smpdtfmt.h"
19 #include "unicode/dtfmtsym.h"
20 #include "unicode/dtptngen.h"
21 #include "unicode/ustring.h"
22 #include "unicode/datefmt.h"
23 #include "cmemory.h"
24 #include "cstring.h"
25 #include "loctest.h"
26
27
28 // This is an API test, not a unit test. It doesn't test very many cases, and doesn't
29 // try to test the full functionality. It just calls each function in the class and
30 // verifies that it works on a basic level.
31
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)32 void IntlTestDateTimePatternGeneratorAPI::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
33 {
34 if (exec) logln("TestSuite DateTimePatternGeneratorAPI");
35 switch (index) {
36 TESTCASE(0, testAPI);
37 TESTCASE(1, testOptions);
38 TESTCASE(2, testAllFieldPatterns);
39 TESTCASE(3, testStaticGetSkeleton);
40 TESTCASE(4, testC);
41 TESTCASE(5, testSkeletonsWithDayPeriods);
42 TESTCASE(6, testGetFieldDisplayNames);
43 TESTCASE(7, testJjMapping);
44 TESTCASE(8, test20640_HourCyclArsEnNH);
45 TESTCASE(9, testFallbackWithDefaultRootLocale);
46 TESTCASE(10, testGetDefaultHourCycle_OnEmptyInstance);
47 default: name = ""; break;
48 }
49 }
50
51 #define MAX_LOCALE 12
52
53 /**
54 * Test various generic API methods of DateTimePatternGenerator for API coverage.
55 */
testAPI()56 void IntlTestDateTimePatternGeneratorAPI::testAPI(/*char *par*/)
57 {
58 UnicodeString patternData[] = {
59 UnicodeString("yM"), // 00
60 UnicodeString("yMMM"), // 01
61 UnicodeString("yMd"), // 02
62 UnicodeString("yMMMd"), // 03
63 UnicodeString("Md"), // 04
64 UnicodeString("MMMd"), // 05
65 UnicodeString("MMMMd"), // 06
66 UnicodeString("yQQQ"), // 07
67 UnicodeString("hhmm"), // 08
68 UnicodeString("HHmm"), // 09
69 UnicodeString("jjmm"), // 10
70 UnicodeString("mmss"), // 11
71 UnicodeString("yyyyMMMM"), // 12
72 UnicodeString("MMMEd"), // 13
73 UnicodeString("Ed"), // 14
74 UnicodeString("jmmssSSS"), // 15
75 UnicodeString("JJmm"), // 16
76 UnicodeString(),
77 };
78
79 const char* testLocale[MAX_LOCALE][4] = {
80 {"en", "US", "", ""}, // 0
81 {"en", "US", "", "calendar=japanese"}, // 1
82 {"de", "DE", "", ""}, // 2
83 {"fi", "", "", ""}, // 3
84 {"es", "", "", ""}, // 4
85 {"ja", "", "", ""}, // 5
86 {"ja", "", "", "calendar=japanese"}, // 6
87 {"zh", "Hans", "CN", ""}, // 7
88 {"zh", "TW", "", "calendar=roc"}, // 8
89 {"ru", "", "", ""}, // 9
90 {"zh", "", "", "calendar=chinese"}, // 10
91 {"ja", "JP", "TRADITIONAL", ""}, // 11
92 };
93
94 // For Weds, Jan 13, 1999, 23:58:59
95 UnicodeString patternResults_en_US[] = {
96 // en_US // 0 en_US
97 UnicodeString("1/1999"), // 00: yM
98 UnicodeString("Jan 1999"), // 01: yMMM
99 UnicodeString("1/13/1999"), // 02: yMd
100 UnicodeString("Jan 13, 1999"), // 03: yMMMd
101 UnicodeString("1/13"), // 04: Md
102 UnicodeString("Jan 13"), // 05: MMMd
103 UnicodeString("January 13"), // 06: MMMMd
104 UnicodeString("Q1 1999"), // 07: yQQQ
105 UnicodeString("11:58 PM"), // 08: hhmm
106 UnicodeString("23:58"), // 09: HHmm
107 UnicodeString("11:58 PM"), // 10: jjmm
108 UnicodeString("58:59"), // 11: mmss
109 UnicodeString("January 1999"), // 12: yyyyMMMM
110 UnicodeString("Wed, Jan 13"), // 13: MMMEd -> EEE, MMM d
111 UnicodeString("13 Wed"), // 14: Ed -> d EEE
112 UnicodeString("11:58:59.123 PM"), // 15: jmmssSSS -> "h:mm:ss.SSS a"
113 UnicodeString("11:58"), // 16: JJmm
114 };
115
116 UnicodeString patternResults_en_US_japanese[] = {
117 // en_US@calendar=japanese // 1 en_US@calendar=japanese
118 UnicodeString("1/11 H"), // 0: yM
119 UnicodeString("Jan 11 Heisei"), // 1: yMMM
120 UnicodeString("1/13/11 H"), // 2: yMd
121 UnicodeString("Jan 13, 11 Heisei"), // 3: yMMMd
122 UnicodeString("1/13"), // 4: Md
123 UnicodeString("Jan 13"), // 5: MMMd
124 UnicodeString("January 13"), // 6: MMMMd
125 UnicodeString("Q1 11 Heisei"), // 7: yQQQ
126 UnicodeString("11:58 PM"), // 8: hhmm
127 UnicodeString("23:58"), // 9: HHmm
128 UnicodeString("11:58 PM"), // 10: jjmm
129 UnicodeString("58:59"), // 11: mmss
130 UnicodeString("January 11 Heisei"), // 12: yyyyMMMM
131 UnicodeString("Wed, Jan 13"), // 13: MMMEd -> EEE, MMM d"
132 UnicodeString("13 Wed"), // 14: Ed -> d EEE
133 UnicodeString("11:58:59.123 PM"), // 15: jmmssSSS -> "h:mm:ss.SSS a"
134 UnicodeString("11:58"), // 16: JJmm
135 };
136
137 UnicodeString patternResults_de_DE[] = {
138 // de_DE // 2 de_DE
139 UnicodeString("1.1999"), // 00: yM
140 UnicodeString("Jan. 1999"), // 01: yMMM
141 UnicodeString("13.1.1999"), // 02: yMd
142 UnicodeString("13. Jan. 1999"), // 03: yMMMd
143 UnicodeString("13.1."), // 04: Md
144 UnicodeString("13. Jan."), // 05: MMMd
145 UnicodeString("13. Januar"), // 06: MMMMd
146 UnicodeString("Q1 1999"), // 07: yQQQ
147 UnicodeString("11:58 PM"), // 08: hhmm
148 UnicodeString("23:58"), // 09: HHmm
149 UnicodeString("23:58"), // 10: jjmm
150 UnicodeString("58:59"), // 11: mmss
151 UnicodeString("Januar 1999"), // 12: yyyyMMMM
152 UnicodeString("Mi., 13. Jan."), // 13: MMMEd -> EEE, d. MMM
153 UnicodeString("Mi., 13."), // 14: Ed -> EEE d.
154 UnicodeString("23:58:59,123"), // 15: jmmssSSS -> "HH:mm:ss,SSS"
155 UnicodeString("23:58"), // 16: JJmm
156 };
157
158 UnicodeString patternResults_fi[] = {
159 // fi // 3 fi
160 UnicodeString("1.1999"), // 00: yM (fixed expected result per ticket:6626:)
161 UnicodeString("tammi 1999"), // 01: yMMM
162 UnicodeString("13.1.1999"), // 02: yMd
163 UnicodeString("13. tammik. 1999"), // 03: yMMMd
164 UnicodeString("13.1."), // 04: Md
165 UnicodeString("13. tammik."), // 05: MMMd
166 UnicodeString("13. tammikuuta"), // 06: MMMMd
167 UnicodeString("1. nelj. 1999"), // 07: yQQQ
168 UnicodeString("11.58 ip."), // 08: hhmm
169 UnicodeString("23.58"), // 09: HHmm
170 UnicodeString("23.58"), // 10: jjmm
171 UnicodeString("58.59"), // 11: mmss
172 UnicodeString("tammikuu 1999"), // 12: yyyyMMMM
173 UnicodeString("ke 13. tammik."), // 13: MMMEd -> EEE d. MMM
174 UnicodeString("ke 13."), // 14: Ed -> ccc d.
175 UnicodeString("23.58.59,123"), // 15: jmmssSSS -> "H.mm.ss,SSS"
176 UnicodeString("23.58"), // 16: JJmm
177 };
178
179 UnicodeString patternResults_es[] = {
180 // es // 4 es
181 UnicodeString("1/1999"), // 00: yM -> "M/y"
182 UnicodeString("ene. 1999"), // 01: yMMM -> "MMM y"
183 UnicodeString("13/1/1999"), // 02: yMd -> "d/M/y"
184 UnicodeString("13 ene. 1999"), // 03: yMMMd -> "d MMM y"
185 UnicodeString("13/1"), // 04: Md -> "d/M"
186 UnicodeString("13 ene."), // 05: MMMd -> "d 'de' MMM"
187 UnicodeString("13 de enero"), // 06: MMMMd -> "d 'de' MMMM"
188 UnicodeString("T1 1999"), // 07: yQQQ -> "QQQ y"
189 CharsToUnicodeString("11:58 p.\\u00A0m."), // 08: hhmm -> "hh:mm a"
190 UnicodeString("23:58"), // 09: HHmm -> "HH:mm"
191 UnicodeString("23:58"), // 10: jjmm -> "HH:mm"
192 UnicodeString("58:59"), // 11: mmss -> "mm:ss"
193 UnicodeString("enero de 1999"), // 12: yyyyMMMM -> "MMMM 'de' yyyy"
194 CharsToUnicodeString("mi\\u00E9., 13 ene."), // 13: MMMEd -> "E, d MMM"
195 CharsToUnicodeString("mi\\u00E9. 13"), // 14: Ed -> "EEE d"
196 UnicodeString("23:58:59,123"), // 15: jmmssSSS -> "H:mm:ss,SSS"
197 UnicodeString("23:58"), // 16: JJmm
198 };
199
200 UnicodeString patternResults_ja[] = {
201 // ja // 5 ja
202 UnicodeString("1999/1"), // 00: yM -> y/M
203 CharsToUnicodeString("1999\\u5E741\\u6708"), // 01: yMMM -> y\u5E74M\u6708
204 UnicodeString("1999/1/13"), // 02: yMd -> y/M/d
205 CharsToUnicodeString("1999\\u5E741\\u670813\\u65E5"), // 03: yMMMd -> y\u5E74M\u6708d\u65E5
206 UnicodeString("1/13"), // 04: Md -> M/d
207 CharsToUnicodeString("1\\u670813\\u65E5"), // 05: MMMd -> M\u6708d\u65E5
208 CharsToUnicodeString("1\\u670813\\u65E5"), // 06: MMMMd -> M\u6708d\u65E5
209 CharsToUnicodeString("1999/Q1"), // 07: yQQQ -> y/QQQ
210 CharsToUnicodeString("\\u5348\\u5F8C11:58"), // 08: hhmm
211 UnicodeString("23:58"), // 09: HHmm -> HH:mm
212 UnicodeString("23:58"), // 10: jjmm
213 UnicodeString("58:59"), // 11: mmss -> mm:ss
214 CharsToUnicodeString("1999\\u5E741\\u6708"), // 12: yyyyMMMM -> y\u5E74M\u6708
215 CharsToUnicodeString("1\\u670813\\u65E5(\\u6C34)"), // 13: MMMEd -> M\u6708d\u65E5(EEE)
216 CharsToUnicodeString("13\\u65E5(\\u6C34)"), // 14: Ed -> d\u65E5(EEE)
217 UnicodeString("23:58:59.123"), // 15: jmmssSSS -> "H:mm:ss.SSS"
218 UnicodeString("23:58"), // 16: JJmm
219 };
220
221 UnicodeString patternResults_ja_japanese[] = {
222 // ja@calendar=japanese // 6 ja@calendar=japanese
223 UnicodeString("H11/1"), // 00: yM -> GGGGGy/m
224 CharsToUnicodeString("\\u5E73\\u621011\\u5E741\\u6708"), // 01: yMMM -> Gy\u5E74M\u6708
225 UnicodeString("H11/1/13"), // 02: yMd -> GGGGGy/m/d
226 CharsToUnicodeString("\\u5E73\\u621011\\u5E741\\u670813\\u65E5"), // 03: yMMMd -> Gy\u5E74M\u6708d\u65E5
227 UnicodeString("1/13"), // 04: Md -> M/d
228 CharsToUnicodeString("1\\u670813\\u65E5"), // 05: MMMd -> M\u6708d\u65E5
229 CharsToUnicodeString("1\\u670813\\u65E5"), // 06: MMMMd -> M\u6708d\u65E5
230 CharsToUnicodeString("\\u5E73\\u621011/Q1"), // 07: yQQQ -> Gy/QQQ
231 CharsToUnicodeString("\\u5348\\u5F8C11:58"), // 08: hhmm ->
232 UnicodeString("23:58"), // 09: HHmm -> HH:mm (as for ja)
233 UnicodeString("23:58"), // 10: jjmm
234 UnicodeString("58:59"), // 11: mmss -> mm:ss (as for ja)
235 CharsToUnicodeString("\\u5E73\\u621011\\u5E741\\u6708"), // 12: yyyyMMMM -> Gyyyy\u5E74M\u6708
236 CharsToUnicodeString("1\\u670813\\u65E5(\\u6C34)"), // 13: MMMEd -> M\u6708d\u65E5(EEE)
237 CharsToUnicodeString("13\\u65E5(\\u6C34)"), // 14: Ed -> d\u65E5(EEE)
238 UnicodeString("23:58:59.123"), // 15: jmmssSSS -> "H:mm:ss.SSS"
239 UnicodeString("23:58"), // 16: JJmm
240 };
241
242 UnicodeString patternResults_zh_Hans_CN[] = {
243 // zh_Hans_CN // 7 zh_Hans_CN
244 CharsToUnicodeString("1999\\u5E741\\u6708"), // 00: yM -> y\u5E74M\u6708
245 CharsToUnicodeString("1999\\u5E741\\u6708"), // 01: yMMM -> yyyy\u5E74MMM (fixed expected result per ticket:6626:)
246 CharsToUnicodeString("1999/1/13"), // 02: yMd
247 CharsToUnicodeString("1999\\u5E741\\u670813\\u65E5"), // 03: yMMMd -> yyyy\u5E74MMMd\u65E5 (fixed expected result per ticket:6626:)
248 UnicodeString("1/13"), // 04: Md
249 CharsToUnicodeString("1\\u670813\\u65E5"), // 05: MMMd -> M\u6708d\u65E5 (fixed expected result per ticket:6626:)
250 CharsToUnicodeString("1\\u670813\\u65E5"), // 06: MMMMd -> M\u6708d\u65E5
251 CharsToUnicodeString("1999\\u5E74\\u7B2C1\\u5B63\\u5EA6"), // 07: yQQQ
252 CharsToUnicodeString("\\u4E0B\\u534811:58"), // 08: hhmm
253 UnicodeString("23:58"), // 09: HHmm
254 CharsToUnicodeString("\\u4E0B\\u534811:58"), // 10: jjmm
255 UnicodeString("58:59"), // 11: mmss
256 CharsToUnicodeString("1999\\u5E741\\u6708"), // 12: yyyyMMMM -> yyyy\u5E74MMM
257 CharsToUnicodeString("1\\u670813\\u65E5\\u5468\\u4E09"), // 13: MMMEd -> MMMd\u65E5EEE
258 CharsToUnicodeString("13\\u65E5\\u5468\\u4E09"), // 14: Ed -> d\u65E5EEE
259 CharsToUnicodeString("\\u4E0B\\u534811:58:59.123"), // 15: jmmssSSS -> "ah:mm:ss.SSS"
260 UnicodeString("11:58"), // 16: JJmm
261 };
262
263 UnicodeString patternResults_zh_TW_roc[] = {
264 // zh_TW@calendar=roc // 8 zh_TW@calendar=roc
265 CharsToUnicodeString("\\u6C11\\u570B88/1"), // 00: yM -> Gy/M
266 CharsToUnicodeString("\\u6C11\\u570B88\\u5E741\\u6708"), // 01: yMMM -> Gy\u5E74M\u6708
267 CharsToUnicodeString("\\u6C11\\u570B88/1/13"), // 02: yMd -> Gy/M/d
268 CharsToUnicodeString("\\u6C11\\u570B88\\u5E741\\u670813\\u65E5"), // 03: yMMMd -> Gy\u5E74M\u6708d\u65E5
269 UnicodeString("1/13"), // 04: Md -> M/d
270 CharsToUnicodeString("1\\u670813\\u65E5"), // 05: MMMd ->M\u6708d\u65E5
271 CharsToUnicodeString("1\\u670813\\u65E5"), // 06: MMMMd ->M\u6708d\u65E5
272 CharsToUnicodeString("\\u6C11\\u570B88\\u5E74\\u7B2C1\\u5B63"), // 07: yQQQ -> Gy QQQ
273 CharsToUnicodeString("\\u4E0B\\u534811:58"), // 08: hhmm ->
274 UnicodeString("23:58"), // 09: HHmm ->
275 CharsToUnicodeString("\\u4E0B\\u534811:58"), // 10: jjmm
276 UnicodeString("58:59"), // 11: mmss ->
277 CharsToUnicodeString("\\u6C11\\u570B88\\u5E741\\u6708"), // 12: yyyyMMMM -> Gy\u5E74M\u670
278 CharsToUnicodeString("1\\u670813\\u65E5\\u9031\\u4E09"), // 13: MMMEd -> M\u6708d\u65E5EEE
279 CharsToUnicodeString("13 \\u9031\\u4E09"), // 14: Ed -> d E
280 CharsToUnicodeString("\\u4E0B\\u534811:58:59.123"), // 15: jmmssSSS -> "ah:mm:ss.SSS"
281 UnicodeString("11:58"), // 16: JJmm
282 };
283
284 UnicodeString patternResults_ru[] = {
285 // ru // 9 ru
286 UnicodeString("01.1999"), // 00: yM -> MM.y
287 CharsToUnicodeString("\\u044F\\u043D\\u0432. 1999 \\u0433."), // 01: yMMM -> LLL y
288 UnicodeString("13.01.1999"), // 02: yMd -> dd.MM.y
289 CharsToUnicodeString("13 \\u044F\\u043D\\u0432. 1999 \\u0433."), // 03: yMMMd -> d MMM y
290 UnicodeString("13.01"), // 04: Md -> dd.MM
291 CharsToUnicodeString("13 \\u044F\\u043D\\u0432."), // 05: MMMd -> d MMM
292 CharsToUnicodeString("13 \\u044F\\u043D\\u0432\\u0430\\u0440\\u044F"), // 06: MMMMd -> d MMMM
293 CharsToUnicodeString("1-\\u0439 \\u043A\\u0432. 1999 \\u0433."), // 07: yQQQ -> y QQQ
294 CharsToUnicodeString("11:58 PM"), // 08: hhmm -> hh:mm a
295 UnicodeString("23:58"), // 09: HHmm -> HH:mm
296 UnicodeString("23:58"), // 10: jjmm -> HH:mm
297 UnicodeString("58:59"), // 11: mmss -> mm:ss
298 CharsToUnicodeString("\\u044F\\u043D\\u0432\\u0430\\u0440\\u044C 1999 \\u0433."), // 12: yyyyMMMM -> LLLL y
299 CharsToUnicodeString("\\u0441\\u0440, 13 \\u044F\\u043D\\u0432."), // 13: MMMEd -> ccc, d MMM
300 CharsToUnicodeString("\\u0441\\u0440, 13"), // 14: Ed -> EEE, d
301 UnicodeString("23:58:59,123"), // 15: jmmssSSS -> "H:mm:ss,SSS"
302 UnicodeString("23:58"), // 16: JJmm
303 };
304
305 UnicodeString patternResults_zh_chinese[] = {
306 // zh@calendar=chinese // 10 zh@calendar=chinese
307 CharsToUnicodeString("1998\\u620A\\u5BC5\\u5E74\\u5341\\u4E00\\u6708"), // 00: yMMM
308 CharsToUnicodeString("1998\\u620A\\u5BC5\\u5E74\\u5341\\u4E00\\u6708"), // 01: yMMM
309 CharsToUnicodeString("1998\\u5E74\\u5341\\u4E00\\u670826"), // 02: yMMMd
310 CharsToUnicodeString("1998\\u5E74\\u5341\\u4E00\\u670826"), // 03: yMMMd
311 UnicodeString("11-26"), // 04: Md
312 CharsToUnicodeString("\\u5341\\u4E00\\u670826\\u65E5"), // 05: MMMd
313 CharsToUnicodeString("\\u5341\\u4E00\\u670826\\u65E5"), // 06: MMMMd
314 CharsToUnicodeString("1998\\u620A\\u5BC5\\u5E74\\u7b2c\\u56db\\u5B63\\u5EA6"), // 07: yQQQ
315 CharsToUnicodeString("\\u4E0B\\u534811:58"), // 08: hhmm
316 UnicodeString("23:58"), // 09: HHmm
317 CharsToUnicodeString("\\u4E0B\\u534811:58"), // 10: jjmm
318 UnicodeString("58:59"), // 11: mmss
319 CharsToUnicodeString("1998\\u620A\\u5BC5\\u5E74\\u5341\\u4E00\\u6708"), // 12: yyyyMMMM
320 CharsToUnicodeString("\\u5341\\u4E00\\u670826\\u65E5\\u5468\\u4E09"), // 13: MMMEd
321 CharsToUnicodeString("26\\u65E5\\u5468\\u4E09"), // 14: Ed -> d\u65E5EEE
322 CharsToUnicodeString("\\u4E0B\\u534811:58:59.123"), // 15: jmmssSS
323 UnicodeString("11:58"), // 16: JJmm
324 };
325
326 UnicodeString patternResults_ja_jp_traditional[] = {
327 // ja_JP_TRADITIONAL // 11 ja_JP_TRADITIONAL
328 u"AD1999/1", // 00: yM
329 u"西暦1999年1月", // 01: yMMM
330 u"1999年1月13日", // 02: yMd
331 u"西暦1999年1月13日", // 03: yMMMd
332 u"1/13", // 04: Md
333 u"1月13日", // 05: MMMd
334 u"1月13日", // 06: MMMMd
335 u"西暦1999/Q1", // 07: yQQQ
336 u"午後11:58", // 08: hhmm
337 u"23:58", // 09: HHmm
338 u"23:58", // 10: jjmm
339 u"58:59", // 11: mmss
340 u"西暦1999年1月", // 12: yyyyMMMM
341 u"1月13日(水)", // 13: MMMEd
342 u"13日(水)", // 14: Ed
343 u"23:58:59.123", // 15: jmmssSSS
344 u"23:58", // 16: JJmm
345 };
346
347 UnicodeString* patternResults[] = {
348 patternResults_en_US, // 0
349 patternResults_en_US_japanese, // 1
350 patternResults_de_DE, // 2
351 patternResults_fi, // 3
352 patternResults_es, // 4
353 patternResults_ja, // 5
354 patternResults_ja_japanese, // 6
355 patternResults_zh_Hans_CN, // 7
356 patternResults_zh_TW_roc, // 8
357 patternResults_ru, // 9
358 patternResults_zh_chinese, // 10
359 patternResults_ja_jp_traditional, // 11
360 };
361
362 UnicodeString patternTests2[] = {
363 UnicodeString("yyyyMMMdd"),
364 UnicodeString("yyyyqqqq"),
365 UnicodeString("yMMMdd"),
366 UnicodeString("EyyyyMMMdd"),
367 UnicodeString("yyyyMMdd"),
368 UnicodeString("yyyyMMM"),
369 UnicodeString("yyyyMM"),
370 UnicodeString("yyMM"),
371 UnicodeString("yMMMMMd"),
372 UnicodeString("EEEEEMMMMMd"),
373 UnicodeString("MMMd"),
374 UnicodeString("MMMdhmm"),
375 UnicodeString("EMMMdhmms"),
376 UnicodeString("MMdhmm"),
377 UnicodeString("EEEEMMMdhmms"),
378 UnicodeString("yyyyMMMddhhmmss"),
379 UnicodeString("EyyyyMMMddhhmmss"),
380 UnicodeString("hmm"),
381 UnicodeString("hhmm"),
382 UnicodeString("hhmmVVVV"),
383 UnicodeString(""),
384 };
385 UnicodeString patternResults2[] = {
386 UnicodeString("Oct 14, 1999"),
387 UnicodeString("4th quarter 1999"),
388 UnicodeString("Oct 14, 1999"),
389 UnicodeString("Thu, Oct 14, 1999"),
390 UnicodeString("10/14/1999"),
391 UnicodeString("Oct 1999"),
392 UnicodeString("10/1999"),
393 UnicodeString("10/99"),
394 UnicodeString("O 14, 1999"),
395 UnicodeString("T, O 14"),
396 UnicodeString("Oct 14"),
397 UnicodeString("Oct 14, 6:58 AM"),
398 UnicodeString("Thu, Oct 14, 6:58:59 AM"),
399 UnicodeString("10/14, 6:58 AM"),
400 UnicodeString("Thursday, Oct 14, 6:58:59 AM"),
401 UnicodeString("Oct 14, 1999, 6:58:59 AM"),
402 UnicodeString("Thu, Oct 14, 1999, 6:58:59 AM"),
403 UnicodeString("6:58 AM"),
404 UnicodeString("6:58 AM"),
405 UnicodeString("6:58 AM GMT"),
406 UnicodeString(""),
407 };
408
409 // results for getSkeletons() and getPatternForSkeleton()
410 const UnicodeString testSkeletonsResults[] = {
411 UnicodeString("HH:mm"),
412 UnicodeString("MMMMd"),
413 UnicodeString("MMMMMdd"),
414 };
415
416 const UnicodeString testBaseSkeletonsResults[] = {
417 UnicodeString("Hm"),
418 UnicodeString("MMMMd"),
419 UnicodeString("MMMMMd"),
420 };
421
422 const char* testGetSkeletonAndBase[][3] = {
423 // pattern skeleton baseSkeleton
424 { "dd-MMM", "MMMdd", "MMMd" },
425 { "dd/MMMM/yy", "yyMMMMdd", "yMMMMd" },
426 { "h", "h", "h" },
427 { "ah", "ah", "ah" },
428 { "aaaah", "aaaah", "aaaah" },
429 { "Bh", "Bh", "Bh" }
430 };
431
432 UnicodeString newDecimal(" "); // space
433 UnicodeString newAppendItemName("hrs.");
434 UnicodeString newAppendItemFormat("{1} {0}");
435 UnicodeString newDateTimeFormat("{1} {0}");
436 UErrorCode status = U_ZERO_ERROR;
437 UnicodeString conflictingPattern;
438 UDateTimePatternConflict conflictingStatus = UDATPG_NO_CONFLICT;
439 (void)conflictingStatus; // Suppress set but not used warning.
440
441 // ======= Test CreateInstance with default locale
442 logln("Testing DateTimePatternGenerator createInstance from default locale");
443
444 DateTimePatternGenerator *instFromDefaultLocale=DateTimePatternGenerator::createInstance(status);
445 if (U_FAILURE(status)) {
446 dataerrln("ERROR: Could not create DateTimePatternGenerator (default) - exitting");
447 return;
448 }
449 else {
450 delete instFromDefaultLocale;
451 }
452
453 // ======= Test CreateInstance with given locale
454 logln("Testing DateTimePatternGenerator createInstance from French locale");
455 status = U_ZERO_ERROR;
456 DateTimePatternGenerator *instFromLocale=DateTimePatternGenerator::createInstance(Locale::getFrench(), status);
457 if (U_FAILURE(status)) {
458 dataerrln("ERROR: Could not create DateTimePatternGenerator (Locale::getFrench()) - exitting");
459 return;
460 }
461
462 // ======= Test clone DateTimePatternGenerator
463 logln("Testing DateTimePatternGenerator::clone()");
464 status = U_ZERO_ERROR;
465
466
467 UnicodeString decimalSymbol = instFromLocale->getDecimal();
468 UnicodeString newDecimalSymbol = UnicodeString("*");
469 decimalSymbol = instFromLocale->getDecimal();
470 instFromLocale->setDecimal(newDecimalSymbol);
471 DateTimePatternGenerator *cloneDTPatternGen=instFromLocale->clone();
472 decimalSymbol = cloneDTPatternGen->getDecimal();
473 if (decimalSymbol != newDecimalSymbol) {
474 errln("ERROR: inconsistency is found in cloned object.");
475 }
476 if ( !(*cloneDTPatternGen == *instFromLocale) ) {
477 errln("ERROR: inconsistency is found in cloned object.");
478 }
479
480 if ( *cloneDTPatternGen != *instFromLocale ) {
481 errln("ERROR: inconsistency is found in cloned object.");
482 }
483
484 delete instFromLocale;
485 delete cloneDTPatternGen;
486
487 // ======= Test simple use cases
488 logln("Testing simple use cases");
489 status = U_ZERO_ERROR;
490 Locale deLocale=Locale::getGermany();
491 UDate sampleDate=LocaleTest::date(99, 9, 13, 23, 58, 59);
492 DateTimePatternGenerator *gen = DateTimePatternGenerator::createInstance(deLocale, status);
493 if (U_FAILURE(status)) {
494 dataerrln("ERROR: Could not create DateTimePatternGenerator (Locale::getGermany()) - exitting");
495 return;
496 }
497 UnicodeString findPattern = gen->getBestPattern(UnicodeString("MMMddHmm"), status);
498 SimpleDateFormat *format = new SimpleDateFormat(findPattern, deLocale, status);
499 if (U_FAILURE(status)) {
500 dataerrln("ERROR: Could not create SimpleDateFormat (Locale::getGermany())");
501 delete gen;
502 return;
503 }
504 TimeZone *zone = TimeZone::createTimeZone(UnicodeString("ECT"));
505 if (zone==NULL) {
506 dataerrln("ERROR: Could not create TimeZone ECT");
507 delete gen;
508 delete format;
509 return;
510 }
511 format->setTimeZone(*zone);
512 UnicodeString dateReturned, expectedResult;
513 dateReturned.remove();
514 dateReturned = format->format(sampleDate, dateReturned, status);
515 expectedResult=UnicodeString("14. Okt., 08:58", -1, US_INV);
516 if ( dateReturned != expectedResult ) {
517 errln("ERROR: Simple test in getBestPattern with Locale::getGermany()).");
518 }
519 // add new pattern
520 status = U_ZERO_ERROR;
521 conflictingStatus = gen->addPattern(UnicodeString("d'. von' MMMM", -1, US_INV), true, conflictingPattern, status);
522 if (U_FAILURE(status)) {
523 errln("ERROR: Could not addPattern - d\'. von\' MMMM");
524 }
525 status = U_ZERO_ERROR;
526 UnicodeString testPattern=gen->getBestPattern(UnicodeString("MMMMdd"), status);
527 testPattern=gen->getBestPattern(UnicodeString("MMMddHmm"), status);
528 format->applyPattern(gen->getBestPattern(UnicodeString("MMMMdHmm"), status));
529 dateReturned.remove();
530 dateReturned = format->format(sampleDate, dateReturned, status);
531 expectedResult=UnicodeString("14. von Oktober, 08:58", -1, US_INV);
532 if ( dateReturned != expectedResult ) {
533 errln(UnicodeString("ERROR: Simple test addPattern failed!: d\'. von\' MMMM Got: ") + dateReturned + UnicodeString(" Expected: ") + expectedResult);
534 }
535 delete format;
536
537 // get a pattern and modify it
538 format = (SimpleDateFormat *)DateFormat::createDateTimeInstance(DateFormat::kFull, DateFormat::kFull,
539 deLocale);
540 format->setTimeZone(*zone);
541 UnicodeString pattern;
542 pattern = format->toPattern(pattern);
543 dateReturned.remove();
544 dateReturned = format->format(sampleDate, dateReturned, status);
545 expectedResult=CharsToUnicodeString("Donnerstag, 14. Oktober 1999 um 08:58:59 Mitteleurop\\u00E4ische Sommerzeit");
546 if ( dateReturned != expectedResult ) {
547 errln("ERROR: Simple test uses full date format.");
548 errln(UnicodeString(" Got: ") + dateReturned + UnicodeString(" Expected: ") + expectedResult);
549 }
550
551 // modify it to change the zone.
552 UnicodeString newPattern = gen->replaceFieldTypes(pattern, UnicodeString("vvvv"), status);
553 format->applyPattern(newPattern);
554 dateReturned.remove();
555 dateReturned = format->format(sampleDate, dateReturned, status);
556 expectedResult=CharsToUnicodeString("Donnerstag, 14. Oktober 1999 um 08:58:59 Mitteleurop\\u00E4ische Zeit");
557 if ( dateReturned != expectedResult ) {
558 errln("ERROR: Simple test modify the timezone!");
559 errln(UnicodeString(" Got: ")+ dateReturned + UnicodeString(" Expected: ") + expectedResult);
560 }
561
562 // setDeciaml(), getDeciaml()
563 gen->setDecimal(newDecimal);
564 if (newDecimal != gen->getDecimal()) {
565 errln("ERROR: unexpected result from setDecimal() and getDecimal()!.\n");
566 }
567
568 // setAppenItemName() , getAppendItemName()
569 gen->setAppendItemName(UDATPG_HOUR_FIELD, newAppendItemName);
570 if (newAppendItemName != gen->getAppendItemName(UDATPG_HOUR_FIELD)) {
571 errln("ERROR: unexpected result from setAppendItemName() and getAppendItemName()!.\n");
572 }
573
574 // setAppenItemFormat() , getAppendItemFormat()
575 gen->setAppendItemFormat(UDATPG_HOUR_FIELD, newAppendItemFormat);
576 if (newAppendItemFormat != gen->getAppendItemFormat(UDATPG_HOUR_FIELD)) {
577 errln("ERROR: unexpected result from setAppendItemFormat() and getAppendItemFormat()!.\n");
578 }
579
580 // setDateTimeFormat() , getDateTimeFormat()
581 gen->setDateTimeFormat(newDateTimeFormat);
582 if (newDateTimeFormat != gen->getDateTimeFormat()) {
583 errln("ERROR: unexpected result from setDateTimeFormat() and getDateTimeFormat()!.\n");
584 }
585
586 // ======== Test getSkeleton and getBaseSkeleton
587
588 int32_t i, count = UPRV_LENGTHOF(testGetSkeletonAndBase);
589 for (i = 0; i < count; i++) {
590 status = U_ZERO_ERROR;
591 pattern = UnicodeString(testGetSkeletonAndBase[i][0]);
592 UnicodeString expectedSkeleton = UnicodeString(testGetSkeletonAndBase[i][1]);
593 UnicodeString expectedBaseSkeleton = UnicodeString(testGetSkeletonAndBase[i][2]);
594 UnicodeString retSkeleton = gen->getSkeleton(pattern, status);
595 if(U_FAILURE(status) || retSkeleton != expectedSkeleton ) {
596 errln("ERROR: Unexpected result from getSkeleton().\n");
597 errln(UnicodeString(" Got: ") + retSkeleton + UnicodeString(" Expected: ") + expectedSkeleton );
598 }
599 retSkeleton = gen->getBaseSkeleton(pattern, status);
600 if(U_FAILURE(status) || retSkeleton != expectedBaseSkeleton) {
601 errln("ERROR: Unexpected result from getBaseSkeleton().\n");
602 errln(UnicodeString(" Got: ") + retSkeleton + UnicodeString(" Expected:")+ expectedBaseSkeleton);
603 }
604 }
605
606 delete format;
607 delete zone;
608 delete gen;
609
610 {
611 // Trac# 6104
612 status = U_ZERO_ERROR;
613 pattern = UnicodeString("YYYYMMM");
614 UnicodeString expR = CharsToUnicodeString("1999\\u5E741\\u6708"); // fixed expected result per ticket:6626:
615 Locale loc("ja");
616 UDate testDate1= LocaleTest::date(99, 0, 13, 23, 58, 59);
617 DateTimePatternGenerator *patGen=DateTimePatternGenerator::createInstance(loc, status);
618 if(U_FAILURE(status)) {
619 dataerrln("ERROR: Could not create DateTimePatternGenerator");
620 return;
621 }
622 UnicodeString bPattern = patGen->getBestPattern(pattern, status);
623 UnicodeString rDate;
624 SimpleDateFormat sdf(bPattern, loc, status);
625 rDate.remove();
626 rDate = sdf.format(testDate1, rDate);
627
628 logln(UnicodeString(" ja locale with skeleton: YYYYMMM Best Pattern:") + bPattern);
629 logln(UnicodeString(" Formatted date:") + rDate);
630
631 if ( expR!= rDate ) {
632 errln(UnicodeString("\nERROR: Test Japanese month hack Got: ") + rDate +
633 UnicodeString(" Expected: ") + expR );
634 }
635
636 delete patGen;
637 }
638 { // Trac# 6104
639 Locale loc("zh");
640 UnicodeString expR = CharsToUnicodeString("1999\\u5E741\\u6708"); // fixed expected result per ticket:6626:
641 UDate testDate1= LocaleTest::date(99, 0, 13, 23, 58, 59);
642 DateTimePatternGenerator *patGen=DateTimePatternGenerator::createInstance(loc, status);
643 if(U_FAILURE(status)) {
644 dataerrln("ERROR: Could not create DateTimePatternGenerator");
645 return;
646 }
647 UnicodeString bPattern = patGen->getBestPattern(pattern, status);
648 UnicodeString rDate;
649 SimpleDateFormat sdf(bPattern, loc, status);
650 rDate.remove();
651 rDate = sdf.format(testDate1, rDate);
652
653 logln(UnicodeString(" zh locale with skeleton: YYYYMMM Best Pattern:") + bPattern);
654 logln(UnicodeString(" Formatted date:") + rDate);
655 if ( expR!= rDate ) {
656 errln(UnicodeString("\nERROR: Test Chinese month hack Got: ") + rDate +
657 UnicodeString(" Expected: ") + expR );
658 }
659 delete patGen;
660 }
661
662 {
663 // Trac# 6172 duplicate time pattern
664 status = U_ZERO_ERROR;
665 pattern = UnicodeString("hmv");
666 UnicodeString expR = UnicodeString("h:mm a v"); // avail formats has hm -> "h:mm a" (fixed expected result per ticket:6626:)
667 Locale loc("en");
668 DateTimePatternGenerator *patGen=DateTimePatternGenerator::createInstance(loc, status);
669 if(U_FAILURE(status)) {
670 dataerrln("ERROR: Could not create DateTimePatternGenerator");
671 return;
672 }
673 UnicodeString bPattern = patGen->getBestPattern(pattern, status);
674 logln(UnicodeString(" en locale with skeleton: hmv Best Pattern:") + bPattern);
675
676 if ( expR!= bPattern ) {
677 errln(UnicodeString("\nERROR: Test EN time format Got: ") + bPattern +
678 UnicodeString(" Expected: ") + expR );
679 }
680
681 delete patGen;
682 }
683
684
685 // ======= Test various skeletons.
686 logln("Testing DateTimePatternGenerator with various skeleton");
687
688 status = U_ZERO_ERROR;
689 int32_t localeIndex=0;
690 int32_t resultIndex=0;
691 UnicodeString resultDate;
692 UDate testDate= LocaleTest::date(99, 0, 13, 23, 58, 59) + 123.0;
693 while (localeIndex < MAX_LOCALE )
694 {
695 resultIndex=0;
696 int32_t dataIndex=0;
697 UnicodeString bestPattern;
698
699 Locale loc(testLocale[localeIndex][0], testLocale[localeIndex][1], testLocale[localeIndex][2], testLocale[localeIndex][3]);
700 logln("\n\n Locale: %s_%s_%s@%s", testLocale[localeIndex][0], testLocale[localeIndex][1], testLocale[localeIndex][2], testLocale[localeIndex][3]);
701 DateTimePatternGenerator *patGen=DateTimePatternGenerator::createInstance(loc, status);
702 if(U_FAILURE(status)) {
703 dataerrln("ERROR: Could not create DateTimePatternGenerator with locale index:%d . - exitting\n", localeIndex);
704 return;
705 }
706 while (patternData[dataIndex].length() > 0) {
707 log(patternData[dataIndex]);
708 bestPattern = patGen->getBestPattern(patternData[dataIndex++], status);
709 logln(UnicodeString(" -> ") + bestPattern);
710
711 SimpleDateFormat sdf(bestPattern, loc, status);
712 resultDate.remove();
713 resultDate = sdf.format(testDate, resultDate);
714 if ( resultDate != patternResults[localeIndex][resultIndex] ) {
715 auto* calendar = sdf.getCalendar();
716 errln(UnicodeString("\nERROR: Test various skeletons[") + (dataIndex-1) + UnicodeString("], localeIndex ") + localeIndex +
717 u". Got: \"" + resultDate +
718 u"\" with calendar " + calendar->getType() +
719 u" Expected: \"" + patternResults[localeIndex][resultIndex] + u"\"");
720 }
721
722 resultIndex++;
723 }
724 delete patGen;
725 localeIndex++;
726 }
727
728 // ======= More tests ticket#6110
729 logln("Testing DateTimePatternGenerator with various skeleton");
730
731 status = U_ZERO_ERROR;
732 localeIndex=0;
733 resultIndex=0;
734 testDate= LocaleTest::date(99, 9, 13, 23, 58, 59);
735 {
736 int32_t dataIndex=0;
737 UnicodeString bestPattern;
738 logln("\n\n Test various skeletons for English locale...");
739 DateTimePatternGenerator *patGen=DateTimePatternGenerator::createInstance(Locale::getEnglish(), status);
740 if(U_FAILURE(status)) {
741 dataerrln("ERROR: Could not create DateTimePatternGenerator with locale English . - exitting\n");
742 return;
743 }
744 TimeZone *enZone = TimeZone::createTimeZone(UnicodeString("ECT/GMT"));
745 if (enZone==NULL) {
746 dataerrln("ERROR: Could not create TimeZone ECT");
747 delete patGen;
748 return;
749 }
750 SimpleDateFormat *enFormat = (SimpleDateFormat *)DateFormat::createDateTimeInstance(DateFormat::kFull,
751 DateFormat::kFull, Locale::getEnglish());
752 enFormat->setTimeZone(*enZone);
753 while (patternTests2[dataIndex].length() > 0) {
754 logln(patternTests2[dataIndex]);
755 bestPattern = patGen->getBestPattern(patternTests2[dataIndex], status);
756 logln(UnicodeString(" -> ") + bestPattern);
757 enFormat->applyPattern(bestPattern);
758 resultDate.remove();
759 resultDate = enFormat->format(testDate, resultDate);
760 if ( resultDate != patternResults2[resultIndex] ) {
761 errln(UnicodeString("\nERROR: Test various skeletons[") + dataIndex
762 + UnicodeString("]. Got: ") + resultDate + UnicodeString(" Expected: ") +
763 patternResults2[resultIndex] );
764 }
765 dataIndex++;
766 resultIndex++;
767 }
768 delete patGen;
769 delete enZone;
770 delete enFormat;
771 }
772
773
774
775 // ======= Test random skeleton
776 DateTimePatternGenerator *randDTGen= DateTimePatternGenerator::createInstance(status);
777 if (U_FAILURE(status)) {
778 dataerrln("ERROR: Could not create DateTimePatternGenerator (Locale::getFrench()) - exitting");
779 return;
780 }
781 UChar newChar;
782 for (i=0; i<10; ++i) {
783 UnicodeString randomSkeleton;
784 int32_t len = rand() % 20;
785 for (int32_t j=0; j<len; ++j ) {
786 while ((newChar = (UChar)(rand()%0x7f))>=(UChar)0x20) {
787 randomSkeleton += newChar;
788 }
789 }
790 UnicodeString bestPattern = randDTGen->getBestPattern(randomSkeleton, status);
791 }
792 delete randDTGen;
793
794 // UnicodeString randomString=Unicode
795 // ======= Test getStaticClassID()
796
797 logln("Testing getStaticClassID()");
798 status = U_ZERO_ERROR;
799 DateTimePatternGenerator *test= DateTimePatternGenerator::createInstance(status);
800
801 if(test->getDynamicClassID() != DateTimePatternGenerator::getStaticClassID()) {
802 errln("ERROR: getDynamicClassID() didn't return the expected value");
803 }
804 delete test;
805
806 // ====== Test createEmptyInstance()
807
808 logln("Testing createEmptyInstance()");
809 status = U_ZERO_ERROR;
810
811 test = DateTimePatternGenerator::createEmptyInstance(status);
812 if(U_FAILURE(status)) {
813 errln("ERROR: Fail to create an empty instance ! - exitting.\n");
814 delete test;
815 return;
816 }
817
818 conflictingStatus = test->addPattern(UnicodeString("MMMMd"), true, conflictingPattern, status);
819 status = U_ZERO_ERROR;
820 testPattern=test->getBestPattern(UnicodeString("MMMMdd"), status);
821 conflictingStatus = test->addPattern(UnicodeString("HH:mm"), true, conflictingPattern, status);
822 conflictingStatus = test->addPattern(UnicodeString("MMMMMdd"), true, conflictingPattern, status); //duplicate pattern
823 StringEnumeration *output=NULL;
824 output = test->getRedundants(status);
825 expectedResult=UnicodeString("MMMMd");
826 if (output != NULL) {
827 output->reset(status);
828 const UnicodeString *dupPattern=output->snext(status);
829 if ( (dupPattern==NULL) || (*dupPattern != expectedResult) ) {
830 errln("ERROR: Fail in getRedundants !\n");
831 }
832 }
833
834 // ======== Test getSkeletons and getBaseSkeletons
835 StringEnumeration* ptrSkeletonEnum = test->getSkeletons(status);
836 if(U_FAILURE(status)) {
837 errln("ERROR: Fail to get skeletons !\n");
838 }
839 UnicodeString returnPattern, *ptrSkeleton;
840 ptrSkeletonEnum->reset(status);
841 count=ptrSkeletonEnum->count(status);
842 for (i=0; i<count; ++i) {
843 ptrSkeleton = (UnicodeString *)ptrSkeletonEnum->snext(status);
844 returnPattern = test->getPatternForSkeleton(*ptrSkeleton);
845 if ( returnPattern != testSkeletonsResults[i] ) {
846 errln(UnicodeString("ERROR: Unexpected result from getSkeletons and getPatternForSkeleton\nGot: ") + returnPattern
847 + UnicodeString("\nExpected: ") + testSkeletonsResults[i]
848 + UnicodeString("\n"));
849 }
850 }
851 StringEnumeration* ptrBaseSkeletonEnum = test->getBaseSkeletons(status);
852 if(U_FAILURE(status)) {
853 errln("ERROR: Fail to get base skeletons !\n");
854 }
855 count=ptrBaseSkeletonEnum->count(status);
856 for (i=0; i<count; ++i) {
857 ptrSkeleton = (UnicodeString *)ptrBaseSkeletonEnum->snext(status);
858 if ( *ptrSkeleton != testBaseSkeletonsResults[i] ) {
859 errln("ERROR: Unexpected result from getBaseSkeletons() !\n");
860 }
861 }
862
863 // ========= DateTimePatternGenerator sample code in Userguide
864 // set up the generator
865 Locale locale = Locale::getFrench();
866 status = U_ZERO_ERROR;
867 DateTimePatternGenerator *generator = DateTimePatternGenerator::createInstance( locale, status);
868
869 // get a pattern for an abbreviated month and day
870 pattern = generator->getBestPattern(UnicodeString("MMMd"), status);
871 SimpleDateFormat formatter(pattern, locale, status);
872
873 zone = TimeZone::createTimeZone(UnicodeString("GMT"));
874 formatter.setTimeZone(*zone);
875 // use it to format (or parse)
876 UnicodeString formatted;
877 formatted = formatter.format(Calendar::getNow(), formatted, status);
878 // for French, the result is "13 sept."
879 formatted.remove();
880 // cannot use the result from getNow() because the value change evreyday.
881 testDate= LocaleTest::date(99, 0, 13, 23, 58, 59);
882 formatted = formatter.format(testDate, formatted, status);
883 expectedResult=UnicodeString("14 janv.");
884 if ( formatted != expectedResult ) {
885 errln("ERROR: Userguide sample code result!");
886 errln(UnicodeString(" Got: ")+ formatted + UnicodeString(" Expected: ") + expectedResult);
887 }
888
889 delete zone;
890 delete output;
891 delete ptrSkeletonEnum;
892 delete ptrBaseSkeletonEnum;
893 delete test;
894 delete generator;
895 }
896
897 /**
898 * Test handling of options
899 *
900 * For reference, as of ICU 4.3.3,
901 * root/gregorian has
902 * Hm{"H:mm"}
903 * Hms{"H:mm:ss"}
904 * hm{"h:mm a"}
905 * hms{"h:mm:ss a"}
906 * en/gregorian has
907 * Hm{"H:mm"}
908 * Hms{"H:mm:ss"}
909 * hm{"h:mm a"}
910 * be/gregorian has
911 * HHmmss{"HH.mm.ss"}
912 * Hm{"HH.mm"}
913 * hm{"h.mm a"}
914 * hms{"h.mm.ss a"}
915 */
916 typedef struct DTPtnGenOptionsData {
917 const char *locale;
918 const char *skel;
919 const char *expectedPattern;
920 UDateTimePatternMatchOptions options;
921 } DTPtnGenOptionsData;
testOptions()922 void IntlTestDateTimePatternGeneratorAPI::testOptions(/*char *par*/)
923 {
924 DTPtnGenOptionsData testData[] = {
925 // locale skel expectedPattern options
926 { "en", "Hmm", "HH:mm", UDATPG_MATCH_NO_OPTIONS },
927 { "en", "HHmm", "HH:mm", UDATPG_MATCH_NO_OPTIONS },
928 { "en", "hhmm", "h:mm a", UDATPG_MATCH_NO_OPTIONS },
929 { "en", "Hmm", "HH:mm", UDATPG_MATCH_HOUR_FIELD_LENGTH },
930 { "en", "HHmm", "HH:mm", UDATPG_MATCH_HOUR_FIELD_LENGTH },
931 { "en", "hhmm", "hh:mm a", UDATPG_MATCH_HOUR_FIELD_LENGTH },
932 { "da", "Hmm", "HH.mm", UDATPG_MATCH_NO_OPTIONS },
933 { "da", "HHmm", "HH.mm", UDATPG_MATCH_NO_OPTIONS },
934 { "da", "hhmm", "h.mm a", UDATPG_MATCH_NO_OPTIONS },
935 { "da", "Hmm", "H.mm", UDATPG_MATCH_HOUR_FIELD_LENGTH },
936 { "da", "HHmm", "HH.mm", UDATPG_MATCH_HOUR_FIELD_LENGTH },
937 { "da", "hhmm", "hh.mm a", UDATPG_MATCH_HOUR_FIELD_LENGTH },
938 //
939 { "en", "yyyy", "yyyy", UDATPG_MATCH_NO_OPTIONS },
940 { "en", "YYYY", "YYYY", UDATPG_MATCH_NO_OPTIONS },
941 { "en", "U", "y", UDATPG_MATCH_NO_OPTIONS },
942 { "en@calendar=japanese", "yyyy", "y G", UDATPG_MATCH_NO_OPTIONS },
943 { "en@calendar=japanese", "YYYY", "Y G", UDATPG_MATCH_NO_OPTIONS },
944 { "en@calendar=japanese", "U", "y G", UDATPG_MATCH_NO_OPTIONS },
945 { "en@calendar=chinese", "yyyy", "r(U)", UDATPG_MATCH_NO_OPTIONS },
946 { "en@calendar=chinese", "YYYY", "Y(Y)", UDATPG_MATCH_NO_OPTIONS }, // not a good result, want r(Y) or r(U)
947 { "en@calendar=chinese", "U", "r(U)", UDATPG_MATCH_NO_OPTIONS },
948 { "en@calendar=chinese", "Gy", "r(U)", UDATPG_MATCH_NO_OPTIONS },
949 { "en@calendar=chinese", "GU", "r(U)", UDATPG_MATCH_NO_OPTIONS },
950 { "en@calendar=chinese", "ULLL", "MMM U", UDATPG_MATCH_NO_OPTIONS },
951 { "en@calendar=chinese", "yMMM", "MMM r(U)", UDATPG_MATCH_NO_OPTIONS },
952 { "en@calendar=chinese", "GUMMM", "MMM r(U)", UDATPG_MATCH_NO_OPTIONS },
953 { "zh@calendar=chinese", "yyyy", "rU\\u5E74", UDATPG_MATCH_NO_OPTIONS },
954 { "zh@calendar=chinese", "YYYY", "YY\\u5E74", UDATPG_MATCH_NO_OPTIONS }, // not a good result, may want r(Y) or r(U)
955 { "zh@calendar=chinese", "U", "rU\\u5E74", UDATPG_MATCH_NO_OPTIONS },
956 { "zh@calendar=chinese", "Gy", "rU\\u5E74", UDATPG_MATCH_NO_OPTIONS },
957 { "zh@calendar=chinese", "GU", "rU\\u5E74", UDATPG_MATCH_NO_OPTIONS },
958 { "zh@calendar=chinese", "ULLL", "U\\u5E74MMM", UDATPG_MATCH_NO_OPTIONS },
959 { "zh@calendar=chinese", "yMMM", "rU\\u5E74MMM", UDATPG_MATCH_NO_OPTIONS },
960 { "zh@calendar=chinese", "GUMMM", "rU\\u5E74MMM", UDATPG_MATCH_NO_OPTIONS },
961 };
962
963 int count = UPRV_LENGTHOF(testData);
964 const DTPtnGenOptionsData * testDataPtr = testData;
965
966 for (; count-- > 0; ++testDataPtr) {
967 UErrorCode status = U_ZERO_ERROR;
968
969 Locale locale(testDataPtr->locale);
970 UnicodeString skel(testDataPtr->skel);
971 UnicodeString expectedPattern(UnicodeString(testDataPtr->expectedPattern).unescape());
972 UDateTimePatternMatchOptions options = testDataPtr->options;
973
974 DateTimePatternGenerator * dtpgen = DateTimePatternGenerator::createInstance(locale, status);
975 if (U_FAILURE(status)) {
976 dataerrln("Unable to create DateTimePatternGenerator instance for locale(%s): %s", locale.getName(), u_errorName(status));
977 delete dtpgen;
978 continue;
979 }
980 UnicodeString pattern = dtpgen->getBestPattern(skel, options, status);
981 if (pattern.compare(expectedPattern) != 0) {
982 errln( UnicodeString("ERROR in getBestPattern, locale ") + UnicodeString(testDataPtr->locale) +
983 UnicodeString(", skeleton ") + skel +
984 ((options)?UnicodeString(", options!=0"):UnicodeString(", options==0")) +
985 UnicodeString(", expected pattern ") + expectedPattern +
986 UnicodeString(", got ") + pattern );
987 }
988 delete dtpgen;
989 }
990 }
991
992 /**
993 * Test that DTPG can handle all valid pattern character / length combinations
994 *
995 */
996 #define FIELD_LENGTHS_COUNT 6
997 #define FIELD_LENGTH_MAX 8
998 #define MUST_INCLUDE_COUNT 5
999
1000 typedef struct AllFieldsTestItem {
1001 char patternChar;
1002 int8_t fieldLengths[FIELD_LENGTHS_COUNT+1]; // up to FIELD_LENGTHS_COUNT lengths to try
1003 // (length <=FIELD_LENGTH_MAX) plus 0 terminator
1004 char mustIncludeOneOf[MUST_INCLUDE_COUNT+1];// resulting pattern must include at least one of
1005 // these as a pattern char (0-terminated list)
1006 } AllFieldsTestItem;
1007
testAllFieldPatterns()1008 void IntlTestDateTimePatternGeneratorAPI::testAllFieldPatterns(/*char *par*/)
1009 {
1010 const char * localeNames[] = {
1011 "root",
1012 "root@calendar=japanese",
1013 "root@calendar=chinese",
1014 "en",
1015 "en@calendar=japanese",
1016 "en@calendar=chinese",
1017 NULL // terminator
1018 };
1019 AllFieldsTestItem testData[] = {
1020 //pat fieldLengths generated pattern must
1021 //chr to test include one of these
1022 { 'G', {1,2,3,4,5,0}, "G" }, // era
1023 // year
1024 { 'y', {1,2,3,4,0}, "yU" }, // year
1025 { 'Y', {1,2,3,4,0}, "Y" }, // year for week of year
1026 { 'u', {1,2,3,4,5,0}, "yuU" }, // extended year
1027 { 'U', {1,2,3,4,5,0}, "yU" }, // cyclic year name
1028 // quarter
1029 { 'Q', {1,2,3,4,0}, "Qq" }, // x
1030 { 'q', {1,2,3,4,0}, "Qq" }, // standalone
1031 // month
1032 { 'M', {1,2,3,4,5,0}, "ML" }, // x
1033 { 'L', {1,2,3,4,5,0}, "ML" }, // standalone
1034 // week
1035 { 'w', {1,2,0}, "w" }, // week of year
1036 { 'W', {1,0}, "W" }, // week of month
1037 // day
1038 { 'd', {1,2,0}, "d" }, // day of month
1039 { 'D', {1,2,3,0}, "D" }, // day of year
1040 { 'F', {1,0}, "F" }, // day of week in month
1041 { 'g', {7,0}, "g" }, // modified julian day
1042 // weekday
1043 { 'E', {1,2,3,4,5,6}, "Eec" }, // day of week
1044 { 'e', {1,2,3,4,5,6}, "Eec" }, // local day of week
1045 { 'c', {1,2,3,4,5,6}, "Eec" }, // standalone local day of week
1046 // day period
1047 { 'a', {1,2,3,4,5,0}, "a" }, // am or pm
1048 { 'b', {1,2,3,4,5,0}, "b" }, // dayPeriod AM/PM/noon
1049 { 'B', {1,2,3,4,5,0}, "B" }, // dayPeriod ranges
1050 // hour
1051 { 'h', {1,2,0}, "hK" }, // 12 (1-12)
1052 { 'H', {1,2,0}, "Hk" }, // 24 (0-23)
1053 { 'K', {1,2,0}, "hK" }, // 12 (0-11)
1054 { 'k', {1,2,0}, "Hk" }, // 24 (1-24)
1055 { 'j', {1,2,0}, "hHKk" }, // locale default
1056 { 'J', {1,2,0}, "hHKk" }, // locale default, without any dayPeriod
1057 { 'C', {1,2,0}, "hHKk" }, // locale allowed first entry, possibly with b or B
1058 // minute
1059 { 'm', {1,2,0}, "m" }, // x
1060 // second & fractions
1061 { 's', {1,2,0}, "s" }, // x
1062 { 'S', {1,2,3,4,0}, "S" }, // fractional second
1063 { 'A', {8,0}, "A" }, // milliseconds in day
1064 // zone
1065 { 'z', {1,2,3,4,0}, "z" }, // x
1066 { 'Z', {1,2,3,4,5,0}, "Z" }, // x
1067 { 'O', {1,4,0}, "O" }, // x
1068 { 'v', {1,4,0}, "v" }, // x
1069 { 'V', {1,2,3,4,0}, "V" }, // x
1070 { 'X', {1,2,3,4,5,0}, "X" }, // x
1071 { 'x', {1,2,3,4,5,0}, "x" }, // x
1072 };
1073
1074 const char ** localeNamesPtr = localeNames;
1075 const char * localeName;
1076 while ( (localeName = *localeNamesPtr++) != NULL) {
1077 UErrorCode status = U_ZERO_ERROR;
1078 Locale locale = Locale::createFromName(localeName);
1079 DateTimePatternGenerator * dtpg = DateTimePatternGenerator::createInstance(locale, status);
1080 if (U_SUCCESS(status)) {
1081 const AllFieldsTestItem * testDataPtr = testData;
1082 int itemCount = UPRV_LENGTHOF(testData);
1083 for (; itemCount-- > 0; ++testDataPtr) {
1084 char skelBuf[FIELD_LENGTH_MAX];
1085 int32_t chrIndx, lenIndx;
1086 for (chrIndx = 0; chrIndx < FIELD_LENGTH_MAX; chrIndx++) {
1087 skelBuf[chrIndx] = testDataPtr->patternChar;
1088 }
1089 for (lenIndx = 0; lenIndx < FIELD_LENGTHS_COUNT; lenIndx++) {
1090 int32_t skelLen = testDataPtr->fieldLengths[lenIndx];
1091 if (skelLen <= 0) {
1092 break;
1093 }
1094 if (skelLen > FIELD_LENGTH_MAX) {
1095 continue;
1096 }
1097 UnicodeString skeleton(skelBuf, skelLen, US_INV);
1098 UnicodeString pattern = dtpg->getBestPattern(skeleton, status);
1099 if (U_FAILURE(status)) {
1100 errln("DateTimePatternGenerator getBestPattern for locale %s, skelChar %c skelLength %d fails: %s",
1101 locale.getName(), testDataPtr->patternChar, skelLen, u_errorName(status));
1102 } else if (pattern.length() <= 0) {
1103 errln("DateTimePatternGenerator getBestPattern for locale %s, skelChar %c skelLength %d produces 0-length pattern",
1104 locale.getName(), testDataPtr->patternChar, skelLen);
1105 } else {
1106 // test that resulting pattern has at least one char in mustIncludeOneOf
1107 UnicodeString mustIncludeOneOf(testDataPtr->mustIncludeOneOf, -1, US_INV);
1108 int32_t patIndx, patLen = pattern.length();
1109 UBool inQuoted = FALSE;
1110 for (patIndx = 0; patIndx < patLen; patIndx++) {
1111 UChar c = pattern.charAt(patIndx);
1112 if (c == 0x27) {
1113 inQuoted = !inQuoted;
1114 } else if (!inQuoted && c <= 0x007A && c >= 0x0041) {
1115 if (mustIncludeOneOf.indexOf(c) >= 0) {
1116 break;
1117 }
1118 }
1119 }
1120 if (patIndx >= patLen) {
1121 errln(UnicodeString("DateTimePatternGenerator getBestPattern for locale ") +
1122 UnicodeString(locale.getName(),-1,US_INV) +
1123 ", skeleton " + skeleton +
1124 ", produces pattern without required chars: " + pattern);
1125 }
1126
1127 }
1128 }
1129 }
1130 delete dtpg;
1131 } else {
1132 dataerrln("Create DateTimePatternGenerator instance for locale(%s) fails: %s",
1133 locale.getName(), u_errorName(status));
1134 }
1135 }
1136 }
1137
testStaticGetSkeleton()1138 void IntlTestDateTimePatternGeneratorAPI::testStaticGetSkeleton(/*char *par*/)
1139 {
1140 // Verify that staticGetSkeleton() doesn't mangle skeletons. (Ticket #11985)
1141 static const char* const testData[] = {
1142 "jmm",
1143 "jjmm",
1144 "Jmm",
1145 "JJmm"
1146 };
1147
1148 for (size_t i = 0; i < UPRV_LENGTHOF(testData); i++) {
1149 UErrorCode status = U_ZERO_ERROR;
1150 UnicodeString skeleton = DateTimePatternGenerator::staticGetSkeleton(testData[i], status);
1151 if (!assertSuccess("staticGetSkeleton", status)) {
1152 return;
1153 }
1154 assertEquals("Skeleton", testData[i], skeleton);
1155 }
1156 }
1157
testC()1158 void IntlTestDateTimePatternGeneratorAPI::testC() {
1159 const char* tests[][3] = {
1160 // These may change with actual data for Bhmm/bhmm skeletons
1161 {"zh", "Cm", "Bh:mm"},
1162 {"zh", "CCm", "Bhh:mm"},
1163 {"zh", "CCCm", "BBBBh:mm"},
1164 {"zh", "CCCCm", "BBBBhh:mm"},
1165 {"zh", "CCCCCm", "BBBBBh:mm"},
1166 {"zh", "CCCCCCm", "BBBBBhh:mm"},
1167 {"de", "Cm", "HH:mm"},
1168 {"de", "CCm", "HH:mm"},
1169 {"de", "CCCm", "HH:mm"},
1170 {"de", "CCCCm", "HH:mm"},
1171 {"en", "Cm", "h:mm a"},
1172 {"en", "CCm", "hh:mm a"},
1173 {"en", "CCCm", "h:mm aaaa"},
1174 {"en", "CCCCm", "hh:mm aaaa"},
1175 {"en", "CCCCCm", "h:mm aaaaa"},
1176 {"en", "CCCCCCm", "hh:mm aaaaa"},
1177 {"en-BN", "Cm", "h:mm b"},
1178 {"gu-IN", "Cm", "h:mm B"},
1179 {"und-IN", "Cm", "h:mm B"}
1180 };
1181
1182 UErrorCode status = U_ZERO_ERROR;
1183 int32_t numTests = UPRV_LENGTHOF(tests);
1184 for (int32_t i = 0; i < numTests; ++i) {
1185 DateTimePatternGenerator *gen = DateTimePatternGenerator::createInstance(
1186 Locale::forLanguageTag(tests[i][0], status), status);
1187 if (gen == NULL) {
1188 dataerrln("FAIL: DateTimePatternGenerator::createInstance failed for %s", tests[i][0]);
1189 return;
1190 }
1191 UDateTimePatternMatchOptions options = UDATPG_MATCH_HOUR_FIELD_LENGTH;
1192 UnicodeString pattern = gen->getBestPattern(tests[i][1], options, status);
1193 UnicodeString expectedPattern = tests[i][2];
1194
1195 char message[100] = "\0";
1196 strcat(message, tests[i][0]);
1197 strcat(message, "/");
1198 strcat(message, tests[i][1]);
1199 assertEquals(message, expectedPattern, pattern);
1200 delete gen;
1201 }
1202 }
1203
1204 enum { kCharBufMax = 31 };
testSkeletonsWithDayPeriods()1205 void IntlTestDateTimePatternGeneratorAPI::testSkeletonsWithDayPeriods() {
1206 const char * patterns[] = {
1207 // since icu4c getEmptyInstance does not call addCanonicalItems (unlike J), set these here:
1208 "a", // should get internal skeleton a
1209 "H", // should get internalskeleton H
1210 "m", // should get internalskeleton m
1211 "s", // should get internalskeleton s
1212 // patterns from which to construct sample data for a locale
1213 //"H", // should get internalskeleton H
1214 "h a", // should get internalskeleton ah
1215 "B h", // should get internalskeleton Bh
1216 };
1217 const char* testItems[][2] = {
1218 // sample requested skeletons and results
1219 // skel pattern
1220 { "H", "H"},
1221 { "HH", "HH"},
1222 { "aH", "H"},
1223 { "aHH", "HH"},
1224 { "BH", "H"},
1225 { "BHH", "HH"},
1226 { "BBBBH", "H"},
1227 { "h", "h a"},
1228 { "hh", "hh a"},
1229 { "ah", "h a"},
1230 { "ahh", "hh a"},
1231 { "aaaah", "h aaaa"},
1232 { "aaaahh", "hh aaaa"},
1233 { "bh", "h b"},
1234 { "bhh", "hh b"},
1235 { "bbbbh", "h bbbb"},
1236 { "Bh", "B h"},
1237 { "Bhh", "B hh"},
1238 { "BBBBh", "BBBB h"},
1239 { "BBBBhh", "BBBB hh"},
1240 { "a", "a"},
1241 { "aaaaa", "aaaaa"},
1242 { "b", "b"},
1243 { "bbbb", "bbbb"},
1244 { "B", "B"},
1245 { "BBBB", "BBBB"},
1246 };
1247 UErrorCode status = U_ZERO_ERROR;
1248 DateTimePatternGenerator *gen = DateTimePatternGenerator::createEmptyInstance(status);
1249 if (U_FAILURE(status)) {
1250 errln("ERROR: createEmptyInstance fails, status: %s", u_errorName(status));
1251 } else {
1252 int32_t i, len = UPRV_LENGTHOF(patterns);
1253 for (i = 0; i < len; i++) {
1254 UnicodeString conflictingPattern;
1255 (void)gen->addPattern(UnicodeString(patterns[i]), TRUE, conflictingPattern, status);
1256 if (U_FAILURE(status)) {
1257 errln("ERROR: addPattern %s fail, status: %s", patterns[i], u_errorName(status));
1258 break;
1259 }
1260 }
1261 if (U_SUCCESS(status)) {
1262 len = UPRV_LENGTHOF(testItems);
1263 for (i = 0; i < len; i++) {
1264 status = U_ZERO_ERROR;
1265 UDateTimePatternMatchOptions options = UDATPG_MATCH_HOUR_FIELD_LENGTH;
1266 UnicodeString result = gen->getBestPattern(UnicodeString(testItems[i][0]), options, status);
1267 if (U_FAILURE(status)) {
1268 errln("ERROR: getBestPattern %s fail, status: %s", testItems[i][0], u_errorName(status));
1269 } else if (result != UnicodeString(testItems[i][1])) {
1270 char charResult[kCharBufMax+1];
1271 result.extract(0, result.length(), charResult, kCharBufMax);
1272 charResult[kCharBufMax] = 0; // ensure termination
1273 errln("ERROR: getBestPattern %s, expected %s, got %s", testItems[i][0], testItems[i][1], charResult);
1274 }
1275 }
1276 }
1277 }
1278 delete gen;
1279 }
1280
1281 typedef struct FieldDisplayNameData {
1282 const char * locale;
1283 UDateTimePatternField field;
1284 UDateTimePGDisplayWidth width;
1285 const char * expected; // can have escapes such as \\u00E0
1286 } FieldDisplayNameData;
1287 enum { kFieldDisplayNameMax = 32 };
1288
testGetFieldDisplayNames()1289 void IntlTestDateTimePatternGeneratorAPI::testGetFieldDisplayNames() {
1290 const FieldDisplayNameData testData[] = {
1291 /*loc field width expectedName */
1292 { "de", UDATPG_QUARTER_FIELD, UDATPG_WIDE, "Quartal" },
1293 { "de", UDATPG_QUARTER_FIELD, UDATPG_ABBREVIATED, "Quart." },
1294 { "de", UDATPG_QUARTER_FIELD, UDATPG_NARROW, "Q" },
1295 { "en", UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD, UDATPG_WIDE, "weekday of the month" },
1296 { "en", UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD, UDATPG_ABBREVIATED, "wkday. of mo." },
1297 { "en", UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD, UDATPG_NARROW, "wkday. of mo." }, // fallback
1298 { "en_GB", UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD, UDATPG_WIDE, "weekday of the month" },
1299 { "en_GB", UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD, UDATPG_ABBREVIATED, "wkday of mo" }, // override
1300 { "en_GB", UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD, UDATPG_NARROW, "wkday of mo" },
1301 { "it", UDATPG_SECOND_FIELD, UDATPG_WIDE, "secondo" },
1302 { "it", UDATPG_SECOND_FIELD, UDATPG_ABBREVIATED, "s" },
1303 { "it", UDATPG_SECOND_FIELD, UDATPG_NARROW, "s" },
1304 };
1305
1306 int count = UPRV_LENGTHOF(testData);
1307 const FieldDisplayNameData * testDataPtr = testData;
1308 for (; count-- > 0; ++testDataPtr) {
1309 UErrorCode status = U_ZERO_ERROR;
1310 Locale locale(testDataPtr->locale);
1311 DateTimePatternGenerator * dtpg = DateTimePatternGenerator::createInstance(locale, status);
1312 if (U_FAILURE(status)) {
1313 dataerrln("FAIL: DateTimePatternGenerator::createInstance failed for locale %s", testDataPtr->locale);
1314 } else {
1315 UChar expName[kFieldDisplayNameMax+1];
1316 u_unescape(testDataPtr->expected, expName, kFieldDisplayNameMax);
1317 expName[kFieldDisplayNameMax] = 0; // ensure 0 termination
1318 UnicodeString getName = dtpg->getFieldDisplayName(testDataPtr->field, testDataPtr->width);
1319 if (getName.compare(expName, u_strlen(expName)) != 0) {
1320 errln("ERROR: locale %s field %d width %d, expected %s\n",
1321 testDataPtr->locale, testDataPtr->field, testDataPtr->width, testDataPtr->expected);
1322 }
1323 delete dtpg;
1324 }
1325 }
1326 }
1327
1328 static const UChar timeCycleChars[] = { (UChar)0x0048, (UChar)0x0068, (UChar)0x004B, (UChar)0x006B, (UChar)0 };
1329
testJjMapping()1330 void IntlTestDateTimePatternGeneratorAPI::testJjMapping() {
1331 UErrorCode status = U_ZERO_ERROR;
1332 UnicodeString jSkeleton("j");
1333 // First test that j maps correctly by region in a locale for which we do not have data.
1334 {
1335 const char* testLocaleID = "de_US"; // short patterns from fallback locale "de" have "HH"
1336 Locale testLocale(testLocaleID);
1337 LocalPointer<DateTimePatternGenerator> dtpg(DateTimePatternGenerator::createInstance(testLocale, status));
1338 if (U_FAILURE(status)) {
1339 dataerrln("FAIL: DateTimePatternGenerator::createInstance failed for locale %s: %s", testLocaleID, u_errorName(status));
1340 } else {
1341 UnicodeString jPattern = dtpg->getBestPattern(jSkeleton, UDATPG_MATCH_ALL_FIELDS_LENGTH, status); // get pattern with h e.g. "h 'Uhr' a"
1342 if (U_FAILURE(status)) {
1343 errln("FAIL: DateTimePatternGenerator::getBestPattern locale %s, pattern j: %s", testLocaleID, u_errorName(status));
1344 } else {
1345 UnicodeString jPatSkeleton = DateTimePatternGenerator::staticGetSkeleton(jPattern, status); // strip literals, get e.g. "ah"
1346 if (U_FAILURE(status)) {
1347 errln("FAIL: DateTimePatternGenerator::staticGetSkeleton locale %s: %s", testLocaleID, u_errorName(status));
1348 } else if (jPatSkeleton.indexOf(u'h') < 0) { // expect US preferred cycle 'h', not H or other cycle
1349 errln("ERROR: DateTimePatternGenerator::getBestPattern locale %s, pattern j did not use 'h'", testLocaleID);
1350 }
1351 }
1352 }
1353 }
1354
1355 // Next test that in all available Locales, the actual short time pattern uses the same cycle as produced by 'j'
1356 int32_t locCount;
1357 const Locale* localePtr = DateFormat::getAvailableLocales(locCount);
1358 for (; locCount-- > 0; localePtr++) {
1359 const char* localeID = localePtr->getName();
1360 status = U_ZERO_ERROR;
1361 LocalPointer<DateTimePatternGenerator> dtpg(DateTimePatternGenerator::createInstance(*localePtr, status));
1362 if (U_FAILURE(status)) {
1363 dataerrln("FAIL: DateTimePatternGenerator::createInstance failed for locale %s: %s", localeID, u_errorName(status));
1364 continue;
1365 }
1366 LocalPointer<DateFormat> dfmt(DateFormat::createTimeInstance(DateFormat::kShort, *localePtr));
1367 if (U_FAILURE(status)) {
1368 dataerrln("FAIL: DateFormat::createTimeInstance kShort failed for locale %s: %s", localeID, u_errorName(status));
1369 continue;
1370 }
1371 const SimpleDateFormat* sdfmt;
1372 if ((sdfmt = dynamic_cast<const SimpleDateFormat*>(reinterpret_cast<const DateFormat*>(dfmt.getAlias()))) == NULL) {
1373 continue;
1374 }
1375 UnicodeString shortPattern;
1376 shortPattern = sdfmt->toPattern(shortPattern);
1377 UnicodeString jPattern = dtpg->getBestPattern(jSkeleton, status);
1378 if (U_FAILURE(status)) {
1379 errln("FAIL: DateTimePatternGenerator::getBestPattern locale %s, pattern j: %s", localeID, u_errorName(status));
1380 continue;
1381 }
1382 // Now check that shortPattern and jPattern use the same hour cycle
1383 UnicodeString jPatSkeleton = DateTimePatternGenerator::staticGetSkeleton(jPattern, status);
1384 UnicodeString shortPatSkeleton = DateTimePatternGenerator::staticGetSkeleton(shortPattern, status);
1385 if (U_FAILURE(status)) {
1386 errln("FAIL: DateTimePatternGenerator::staticGetSkeleton locale %s: %s", localeID, u_errorName(status));
1387 continue;
1388 }
1389 const UChar* charPtr = timeCycleChars;
1390 for (; *charPtr != (UChar)0; charPtr++) {
1391 if (jPatSkeleton.indexOf(*charPtr) >= 0) {
1392 if (shortPatSkeleton.indexOf(*charPtr) < 0) {
1393 char jcBuf[2], spBuf[32], jpBuf[32];
1394 u_austrncpy(jcBuf, charPtr, 1);
1395 jcBuf[1] = 0;
1396 shortPattern.extract(0, shortPattern.length(), spBuf, 32);
1397 jPattern.extract(0, jPattern.length(), jpBuf, 32);
1398 const char* dfmtCalType = (dfmt->getCalendar())->getType();
1399 errln("ERROR: locale %s, expected j resolved char %s to occur in short time pattern '%s' for %s (best pattern: '%s')", localeID, jcBuf, spBuf, dfmtCalType, jpBuf);
1400 }
1401 break;
1402 }
1403 }
1404 }
1405 }
1406
test20640_HourCyclArsEnNH()1407 void IntlTestDateTimePatternGeneratorAPI::test20640_HourCyclArsEnNH() {
1408 IcuTestErrorCode status(*this, "test20640_HourCyclArsEnNH");
1409
1410 const struct TestCase {
1411 const char* localeName;
1412 const char16_t* expectedDtpgPattern;
1413 const char16_t* expectedTimePattern;
1414 UDateFormatHourCycle expectedDefaultHourCycle;
1415 } cases[] = {
1416 // ars is interesting because it does not have a region, but it aliases
1417 // to ar_SA, which has a region.
1418 {"ars", u"h a", u"h:mm a", UDAT_HOUR_CYCLE_12},
1419 // en_NH is interesting because NH is a deprecated region code;
1420 // formerly New Hebrides, now Vanuatu => VU => h.
1421 {"en_NH", u"h a", u"h:mm a", UDAT_HOUR_CYCLE_12},
1422 // ch_ZH is a typo (should be zh_CN), but we should fail gracefully.
1423 // {"cn_ZH", u"HH", u"H:mm"}, // TODO(ICU-20653): Desired behavior
1424 {"cn_ZH", u"HH", u"h:mm a", UDAT_HOUR_CYCLE_23 }, // Actual behavior
1425 // a non-BCP47 locale without a country code should not fail
1426 {"ja_TRADITIONAL", u"H時", u"H:mm", UDAT_HOUR_CYCLE_23},
1427 };
1428
1429 for (auto& cas : cases) {
1430 status.setScope(cas.localeName);
1431
1432 Locale loc(cas.localeName);
1433 LocalPointer<DateFormat> dtf(DateFormat::createTimeInstance(DateFormat::kShort, loc), status);
1434 LocalPointer<DateTimePatternGenerator> dtpg(DateTimePatternGenerator::createInstance(loc, status));
1435 if (status.errIfFailureAndReset()) {
1436 return;
1437 }
1438
1439 UnicodeString timePattern;
1440 dynamic_cast<SimpleDateFormat*>(dtf.getAlias())->toPattern(timePattern);
1441 UnicodeString dtpgPattern = dtpg->getBestPattern(u"j", status);
1442 if (status.errIfFailureAndReset()) {
1443 return;
1444 }
1445 UDateFormatHourCycle defaultHourCycle = dtpg->getDefaultHourCycle(status);
1446 if (status.errIfFailureAndReset()) {
1447 return;
1448 }
1449
1450 assertEquals(UnicodeString("dtpgPattern ") + cas.localeName,
1451 cas.expectedDtpgPattern, dtpgPattern);
1452 assertEquals(UnicodeString("timePattern ") + cas.localeName,
1453 cas.expectedTimePattern, timePattern);
1454 assertEquals(UnicodeString("defaultHour ") + cas.localeName,
1455 cas.expectedDefaultHourCycle, defaultHourCycle);
1456 }
1457
1458 }
1459
testFallbackWithDefaultRootLocale()1460 void IntlTestDateTimePatternGeneratorAPI::testFallbackWithDefaultRootLocale() {
1461 UErrorCode status = U_ZERO_ERROR;
1462 char original[ULOC_FULLNAME_CAPACITY];
1463
1464 uprv_strcpy(original, uloc_getDefault());
1465 uloc_setDefault("root", &status);
1466 if (U_FAILURE(status)) {
1467 errln("ERROR: Failed to change the default locale to root! Default is: %s\n", uloc_getDefault());
1468 }
1469
1470 DateTimePatternGenerator* dtpg = icu::DateTimePatternGenerator::createInstance("abcdedf", status);
1471
1472 if (U_FAILURE(status)) {
1473 errln("ERROR: expected createInstance with invalid locale to succeed. Status: %s", u_errorName(status));
1474 }
1475 if (status != U_USING_DEFAULT_WARNING) {
1476 errln("ERROR: expected createInstance to return U_USING_DEFAULT_WARNING for invalid locale and default root locale. Status: %s", u_errorName(status));
1477 }
1478
1479 delete dtpg;
1480
1481 uloc_setDefault(original, &status);
1482 if (U_FAILURE(status)) {
1483 errln("ERROR: Failed to change the default locale back to %s\n", original);
1484 }
1485 }
1486
1487 // ICU-21000 Ensure that calling getDefaultHourCycle on an empty instance doesn't call UPRV_UNREACHABLE/abort.
testGetDefaultHourCycle_OnEmptyInstance()1488 void IntlTestDateTimePatternGeneratorAPI::testGetDefaultHourCycle_OnEmptyInstance() {
1489 UErrorCode status = U_ZERO_ERROR;
1490
1491 LocalPointer<DateTimePatternGenerator> dtpg(DateTimePatternGenerator::createEmptyInstance(status), status);
1492 if (U_FAILURE(status)) {
1493 errln("ERROR: createEmptyInstance failed, status: %s", u_errorName(status));
1494 return;
1495 }
1496 (void)dtpg->getDefaultHourCycle(status);
1497 if (!U_FAILURE(status)) {
1498 errln("ERROR: expected getDefaultHourCycle on an empty instance to fail, status: %s", u_errorName(status));
1499 return;
1500 }
1501
1502 status = U_USELESS_COLLATOR_ERROR;
1503 (void)dtpg->getDefaultHourCycle(status);
1504 if (status != U_USELESS_COLLATOR_ERROR) {
1505 errln("ERROR: getDefaultHourCycle shouldn't modify status if it is already failed, status: %s", u_errorName(status));
1506 return;
1507 }
1508 }
1509
1510 #endif /* #if !UCONFIG_NO_FORMATTING */
1511