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