• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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