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