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