1 /*
2  *******************************************************************************
3  * Copyright (C) 2006-2015, Google, International Business Machines Corporation *
4  * and others. All Rights Reserved.                                            *
5  *******************************************************************************
6  */
7 
8 package com.ibm.icu.dev.test.format;
9 
10 import java.text.ParsePosition;
11 import java.util.Collection;
12 import java.util.Date;
13 import java.util.HashSet;
14 import java.util.Iterator;
15 import java.util.LinkedHashMap;
16 import java.util.LinkedHashSet;
17 import java.util.List;
18 import java.util.Locale;
19 import java.util.Map;
20 import java.util.Random;
21 import java.util.Set;
22 
23 import com.ibm.icu.dev.test.TestFmwk;
24 import com.ibm.icu.impl.PatternTokenizer;
25 import com.ibm.icu.impl.Utility;
26 import com.ibm.icu.text.DateFormat;
27 import com.ibm.icu.text.DateTimePatternGenerator;
28 import com.ibm.icu.text.DateTimePatternGenerator.FormatParser;
29 import com.ibm.icu.text.DateTimePatternGenerator.VariableField;
30 import com.ibm.icu.text.SimpleDateFormat;
31 import com.ibm.icu.text.UTF16;
32 import com.ibm.icu.text.UnicodeSet;
33 import com.ibm.icu.util.Calendar;
34 import com.ibm.icu.util.GregorianCalendar;
35 import com.ibm.icu.util.SimpleTimeZone;
36 import com.ibm.icu.util.TimeZone;
37 import com.ibm.icu.util.ULocale;
38 
39 public class DateTimeGeneratorTest extends TestFmwk {
40     public static boolean GENERATE_TEST_DATA;
41     static {
42         try {
43             GENERATE_TEST_DATA = System.getProperty("GENERATE_TEST_DATA") != null;
44         } catch (SecurityException e) {
45             GENERATE_TEST_DATA = false;
46         }
47     };
48     public static int RANDOM_COUNT = 1000;
49     public static boolean DEBUG = false;
50 
main(String[] args)51     public static void main(String[] args) throws Exception {
52         new DateTimeGeneratorTest().run(args);
53     }
54 
TestSimple()55     public void TestSimple() {
56         // some simple use cases
57         ULocale locale = ULocale.GERMANY;
58         TimeZone zone = TimeZone.getTimeZone("Europe/Paris");
59 
60         // make from locale
61         DateTimePatternGenerator gen = DateTimePatternGenerator.getInstance(locale);
62         SimpleDateFormat format = new SimpleDateFormat(gen.getBestPattern("MMMddHmm"), locale);
63         format.setTimeZone(zone);
64         assertEquals("simple format: MMMddHmm", "14. Okt., 08:58", format.format(sampleDate));
65         // (a generator can be built from scratch, but that is not a typical use case)
66 
67         // modify the generator by adding patterns
68         DateTimePatternGenerator.PatternInfo returnInfo = new DateTimePatternGenerator.PatternInfo();
69         gen.addPattern("d'. von' MMMM", true, returnInfo);
70         // the returnInfo is mostly useful for debugging problem cases
71         format.applyPattern(gen.getBestPattern("MMMMdHmm"));
72         assertEquals("modified format: MMMdHmm", "14. von Oktober, 08:58", format.format(sampleDate));
73 
74         // get a pattern and modify it
75         format = (SimpleDateFormat)DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, locale);
76         format.setTimeZone(zone);
77         String pattern = format.toPattern();
78         assertEquals("full-date", "Donnerstag, 14. Oktober 1999 um 08:58:59 Mitteleurop\u00E4ische Sommerzeit", format.format(sampleDate));
79 
80         // modify it to change the zone.
81         String newPattern = gen.replaceFieldTypes(pattern, "vvvv");
82         format.applyPattern(newPattern);
83         assertEquals("full-date: modified zone", "Donnerstag, 14. Oktober 1999 um 08:58:59 Mitteleurop\u00E4ische Zeit", format.format(sampleDate));
84 
85         // add test of basic cases
86 
87         //lang  YYYYMMM MMMd    MMMdhmm hmm hhmm    Full Date-Time
88         // en  Mar 2007    Mar 4   6:05 PM Mar 4   6:05 PM 06:05 PM    Sunday, March 4, 2007 6:05:05 PM PT
89         DateTimePatternGenerator enGen = DateTimePatternGenerator.getInstance(ULocale.ENGLISH);
90         TimeZone enZone = TimeZone.getTimeZone("Etc/GMT");
91         SimpleDateFormat enFormat = (SimpleDateFormat)DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, ULocale.ENGLISH);
92         enFormat.setTimeZone(enZone);
93         String[][] tests = {
94               {"yyyyMMMdd", "Oct 14, 1999"},
95               {"yyyyqqqq", "4th quarter 1999"},
96               {"yMMMdd", "Oct 14, 1999"},
97               {"EyyyyMMMdd", "Thu, Oct 14, 1999"},
98               {"yyyyMMdd", "10/14/1999"},
99               {"yyyyMMM", "Oct 1999"},
100               {"yyyyMM", "10/1999"},
101               {"yyMM", "10/99"},
102               {"yMMMMMd", "O 14, 1999"},  // narrow format
103               {"EEEEEMMMMMd", "T, O 14"},  // narrow format
104               {"MMMd", "Oct 14"},
105               {"MMMdhmm", "Oct 14, 6:58 AM"},
106               {"EMMMdhmms", "Thu, Oct 14, 6:58:59 AM"},
107               {"MMdhmm", "10/14, 6:58 AM"},
108               {"EEEEMMMdhmms", "Thursday, Oct 14, 6:58:59 AM"},
109               {"yyyyMMMddhhmmss", "Oct 14, 1999, 6:58:59 AM"}, // (fixed expected result per ticket 6872<-7180)
110               {"EyyyyMMMddhhmmss", "Thu, Oct 14, 1999, 6:58:59 AM"}, // (fixed expected result per ticket 6872<-7180)
111               {"hmm", "6:58 AM"},
112               {"hhmm", "6:58 AM"}, // (fixed expected result per ticket 6872<-7180)
113               {"hhmmVVVV", "6:58 AM GMT"}, // (fixed expected result per ticket 6872<-7180)
114         };
115         for (int i = 0; i < tests.length; ++i) {
116             final String testSkeleton = tests[i][0];
117             String pat = enGen.getBestPattern(testSkeleton);
118             enFormat.applyPattern(pat);
119             String formattedDate = enFormat.format(sampleDate);
120             assertEquals("Testing skeleton '" + testSkeleton + "' with  " + sampleDate, tests[i][1], formattedDate);
121         }
122     }
123 
TestRoot()124     public void TestRoot() {
125         DateTimePatternGenerator rootGen = DateTimePatternGenerator.getInstance(ULocale.ROOT);
126         SimpleDateFormat rootFormat = new SimpleDateFormat(rootGen.getBestPattern("yMdHms"), ULocale.ROOT);
127         rootFormat.setTimeZone(gmt);
128         // *** expected result should be "1999-10-14 6:58:59" with current data, changed test temporarily to match current result, needs investigation
129         assertEquals("root format: yMdHms", "1999-10-14 06:58:59", rootFormat.format(sampleDate));
130     }
131 
TestEmpty()132     public void TestEmpty() {
133         // now nothing
134         DateTimePatternGenerator nullGen = DateTimePatternGenerator.getEmptyInstance();
135         SimpleDateFormat format = new SimpleDateFormat(nullGen.getBestPattern("yMdHms"), ULocale.ROOT);
136         TimeZone rootZone = TimeZone.getTimeZone("Etc/GMT");
137         format.setTimeZone(rootZone);
138     }
139 
TestPatternParser()140     public void TestPatternParser() {
141         StringBuffer buffer = new StringBuffer();
142         PatternTokenizer pp = new PatternTokenizer()
143         .setIgnorableCharacters(new UnicodeSet("[-]"))
144         .setSyntaxCharacters(new UnicodeSet("[a-zA-Z]"))
145         .setEscapeCharacters(new UnicodeSet("[b#]"))
146         .setUsingQuote(true);
147         logln("Using Quote");
148         for (int i = 0; i < patternTestData.length; ++i) {
149             String patternTest = (String) patternTestData[i];
150             CheckPattern(buffer, pp, patternTest);
151         }
152         String[] randomSet = {"abcdef", "$12!@#-", "'\\"};
153         for (int i = 0; i < RANDOM_COUNT; ++i) {
154             String patternTest = getRandomString(randomSet, 0, 10);
155             CheckPattern(buffer, pp, patternTest);
156         }
157         logln("Using Backslash");
158         pp.setUsingQuote(false).setUsingSlash(true);
159         for (int i = 0; i < patternTestData.length; ++i) {
160             String patternTest = (String) patternTestData[i];
161             CheckPattern(buffer, pp, patternTest);
162         }
163         for (int i = 0; i < RANDOM_COUNT; ++i) {
164             String patternTest = getRandomString(randomSet, 0, 10);
165             CheckPattern(buffer, pp, patternTest);
166         }
167     }
168 
169     Random random = new java.util.Random(-1);
170 
getRandomString(String[] randomList, int minLen, int maxLen)171     private String getRandomString(String[] randomList, int minLen, int maxLen) {
172         StringBuffer result = new StringBuffer();
173         int len = random.nextInt(maxLen + 1 - minLen) + minLen;
174         for (int i = minLen; i < len; ++ i) {
175             String source = randomList[random.nextInt(randomList.length)]; // don't bother with surrogates
176             char ch = source.charAt(random.nextInt(source.length()));
177             UTF16.append(result, ch);
178         }
179         return result.toString();
180     }
181 
CheckPattern(StringBuffer buffer, PatternTokenizer pp, String patternTest)182     private void CheckPattern(StringBuffer buffer, PatternTokenizer pp, String patternTest) {
183         pp.setPattern(patternTest);
184         if (DEBUG && isVerbose()) {
185             showItems(buffer, pp, patternTest);
186         }
187         String normalized = pp.setStart(0).normalize();
188         logln("input:\t<" + patternTest + ">" + "\tnormalized:\t<" + normalized + ">");
189         String doubleNormalized = pp.setPattern(normalized).normalize();
190         if (!normalized.equals(doubleNormalized)) {
191             errln("Normalization not idempotent:\t" + patternTest + "\tnormalized: " + normalized +  "\tnormalized2: " + doubleNormalized);
192             // allow for debugging at the point of failure
193             if (DEBUG) {
194                 pp.setPattern(patternTest);
195                 normalized = pp.setStart(0).normalize();
196                 pp.setPattern(normalized);
197                 showItems(buffer, pp, normalized);
198                 doubleNormalized = pp.normalize();
199             }
200         }
201     }
202 
showItems(StringBuffer buffer, PatternTokenizer pp, String patternTest)203     private void showItems(StringBuffer buffer, PatternTokenizer pp, String patternTest) {
204         logln("input:\t<" + patternTest + ">");
205         while (true) {
206             buffer.setLength(0);
207             int status = pp.next(buffer);
208             if (status == PatternTokenizer.DONE) break;
209             String lit = "";
210             if (status != PatternTokenizer.SYNTAX ) {
211                 lit = "\t<" + pp.quoteLiteral(buffer) + ">";
212             }
213             logln("\t" + statusName[status] + "\t<" + buffer + ">" + lit);
214         }
215     }
216 
217     static final String[] statusName = {"DONE", "SYNTAX", "LITERAL", "BROKEN_QUOTE", "BROKEN_ESCAPE", "UNKNOWN"};
218 
TestBasic()219     public void TestBasic() {
220         ULocale uLocale = null;
221         DateTimePatternGenerator dtfg = null;
222         Date date = null;
223         for (int i = 0; i < dateTestData.length; ++i) {
224             if (dateTestData[i] instanceof ULocale) {
225                 uLocale = (ULocale) dateTestData[i];
226                 dtfg = DateTimePatternGenerator.getInstance(uLocale);
227                 if (GENERATE_TEST_DATA) logln("new ULocale(\"" + uLocale.toString() + "\"),");
228             } else if (dateTestData[i] instanceof Date) {
229                 date = (Date) dateTestData[i];
230                 if (GENERATE_TEST_DATA) logln("new Date(" + date.getTime()+ "L),");
231             } else if (dateTestData[i] instanceof String) {
232                 String testSkeleton = (String) dateTestData[i];
233                 String pattern = dtfg.getBestPattern(testSkeleton);
234                 SimpleDateFormat sdf = new SimpleDateFormat(pattern, uLocale);
235                 String formatted = sdf.format(date);
236                 if (GENERATE_TEST_DATA) logln("new String[] {\"" + testSkeleton + "\", \"" + Utility.escape(formatted) + "\"},");
237                 //logln(uLocale + "\t" + testSkeleton + "\t" + pattern + "\t" + sdf.format(date));
238             } else {
239                 String[] testPair = (String[]) dateTestData[i];
240                 String testSkeleton = testPair[0];
241                 String testFormatted = testPair[1];
242                 String pattern = dtfg.getBestPattern(testSkeleton);
243                 SimpleDateFormat sdf = new SimpleDateFormat(pattern, uLocale);
244                 String formatted = sdf.format(date);
245                 if (GENERATE_TEST_DATA) {
246                     logln("new String[] {\"" + testSkeleton + "\", \"" + Utility.escape(formatted) + "\"},");
247                 } else if (!formatted.equals(testFormatted)) {
248                     errln(uLocale + "\tformatted string doesn't match test case: " + testSkeleton + "\t generated: " +  pattern + "\t expected: " + testFormatted + "\t got: " + formatted);
249                     if (true) { // debug
250                         pattern = dtfg.getBestPattern(testSkeleton);
251                         sdf = new SimpleDateFormat(pattern, uLocale);
252                         formatted = sdf.format(date);
253                     }
254                 }
255                 //logln(uLocale + "\t" + testSkeleton + "\t" + pattern + "\t" + sdf.format(date));
256             }
257         }
258     }
259 
260     static final Object[] patternTestData = {
261         "'$f''#c",
262         "'' 'a",
263         "'.''.'",
264         "\\u0061\\\\",
265         "mm.dd 'dd ' x",
266         "'' ''",
267     };
268 
269     // can be generated by using GENERATE_TEST_DATA. Must be reviewed before adding
270     static final Object[] dateTestData = {
271         new Date(916300739123L), // 1999-01-13T23:58:59.123,0-0800
272 
273         new ULocale("en_US"),
274         new String[] {"yM", "1/1999"},
275         new String[] {"yMMM", "Jan 1999"},
276         new String[] {"yMd", "1/13/1999"},
277         new String[] {"yMMMd", "Jan 13, 1999"},
278         new String[] {"Md", "1/13"},
279         new String[] {"MMMd", "Jan 13"},
280         new String[] {"MMMMd", "January 13"},
281         new String[] {"yQQQ", "Q1 1999"},
282         new String[] {"hhmm", "11:58 PM"},
283         new String[] {"HHmm", "23:58"},
284         new String[] {"jjmm", "11:58 PM"},
285         new String[] {"mmss", "58:59"},
286         new String[] {"yyyyMMMM", "January 1999"}, // (new item for testing 6872<-5702)
287         new String[] {"MMMEd", "Wed, Jan 13"},
288         new String[] {"Ed", "13 Wed"},
289         new String[] {"jmmssSSS", "11:58:59.123 PM"},
290         new String[] {"JJmm", "11:58"},
291 
292         new ULocale("en_US@calendar=japanese"), // (new locale for testing ticket 6872<-5702)
293         new String[] {"yM", "1/11 H"},
294         new String[] {"yMMM", "Jan 11 Heisei"},
295         new String[] {"yMd", "1/13/11 H"},
296         new String[] {"yMMMd", "Jan 13, 11 Heisei"},
297         new String[] {"Md", "1/13"},
298         new String[] {"MMMd", "Jan 13"},
299         new String[] {"MMMMd", "January 13"},
300         new String[] {"yQQQ", "Q1 11 Heisei"},
301         new String[] {"hhmm", "11:58 PM"},
302         new String[] {"HHmm", "23:58"},
303         new String[] {"jjmm", "11:58 PM"},
304         new String[] {"mmss", "58:59"},
305         new String[] {"yyyyMMMM", "January 11 Heisei"},
306         new String[] {"MMMEd", "Wed, Jan 13"},
307         new String[] {"Ed", "13 Wed"},
308         new String[] {"jmmssSSS", "11:58:59.123 PM"},
309         new String[] {"JJmm", "11:58"},
310 
311         new ULocale("de_DE"),
312         new String[] {"yM", "1.1999"},
313         new String[] {"yMMM", "Jan. 1999"},
314         new String[] {"yMd", "13.1.1999"},
315         new String[] {"yMMMd", "13. Jan. 1999"},
316         new String[] {"Md", "13.1."},   // 13.1
317         new String[] {"MMMd", "13. Jan."},
318         new String[] {"MMMMd", "13. Januar"},
319         new String[] {"yQQQ", "Q1 1999"},
320         new String[] {"hhmm", "11:58 nachm."},
321         new String[] {"HHmm", "23:58"},
322         new String[] {"jjmm", "23:58"},
323         new String[] {"mmss", "58:59"},
324         new String[] {"yyyyMMMM", "Januar 1999"}, // (new item for testing 6872<-5702)
325         new String[] {"MMMEd", "Mi., 13. Jan."},
326         new String[] {"Ed", "Mi., 13."},
327         new String[] {"jmmssSSS", "23:58:59,123"},
328         new String[] {"JJmm", "23:58"},
329 
330         new ULocale("fi"),
331         new String[] {"yM", "1.1999"}, // (fixed expected result per ticket 6872<-6626)
332         new String[] {"yMMM", "tammi 1999"}, // (fixed expected result per ticket 6872<-7007)
333         new String[] {"yMd", "13.1.1999"},
334         new String[] {"yMMMd", "13. tammikuuta 1999"},
335         new String[] {"Md", "13.1."},
336         new String[] {"MMMd", "13. tammikuuta"},
337         new String[] {"MMMMd", "13. tammikuuta"},
338         new String[] {"yQQQ", "1. nelj. 1999"},
339         new String[] {"hhmm", "11.58 ip."},
340         new String[] {"HHmm", "23.58"},
341         new String[] {"jjmm", "23.58"},
342         new String[] {"mmss", "58.59"},
343         new String[] {"yyyyMMMM", "tammikuu 1999"}, // (new item for testing 6872<-5702,7007)
344         new String[] {"MMMEd", "ke 13. tammikuuta"},
345         new String[] {"Ed", "ke 13."},
346         new String[] {"jmmssSSS", "23.58.59,123"},
347         new String[] {"JJmm", "23.58"},
348 
349         new ULocale("es"),
350         new String[] {"yM", "1/1999"},
351         new String[] {"yMMM", "ene. 1999"},
352         new String[] {"yMd", "13/1/1999"},
353         new String[] {"yMMMd", "13 ene. 1999"},
354         new String[] {"Md", "13/1"},
355         new String[] {"MMMd", "13 ene."},
356         new String[] {"MMMMd", "13 de enero"},
357         new String[] {"yQQQ", "T1 1999"},
358         new String[] {"hhmm", "11:58 p. m."},
359         new String[] {"HHmm", "23:58"},
360         new String[] {"jjmm", "23:58"},
361         new String[] {"mmss", "58:59"},
362         new String[] {"yyyyMMMM", "enero de 1999"},
363         new String[] {"MMMEd", "mi\u00E9., 13 ene."},
364         new String[] {"Ed", "mi\u00E9. 13"},
365         new String[] {"jmmssSSS", "23:58:59,123"},
366         new String[] {"JJmm", "23:58"},
367 
368         new ULocale("ja"), // (new locale for testing ticket 6872<-6626)
369         new String[] {"yM", "1999/1"},
370         new String[] {"yMMM", "1999\u5E741\u6708"},
371         new String[] {"yMd", "1999/1/13"},
372         new String[] {"yMMMd", "1999\u5E741\u670813\u65E5"},
373         new String[] {"Md", "1/13"},
374         new String[] {"MMMd", "1\u670813\u65E5"},
375         new String[] {"MMMMd", "1\u670813\u65E5"},
376         new String[] {"yQQQ", "1999/Q1"},
377         new String[] {"hhmm", "\u5348\u5F8C11:58"},
378         new String[] {"HHmm", "23:58"},
379         new String[] {"jjmm", "23:58"},
380         new String[] {"mmss", "58:59"},
381         new String[] {"yyyyMMMM", "1999\u5E741\u6708"}, // (new item for testing 6872<-5702)
382         new String[] {"MMMEd", "1\u670813\u65E5(\u6C34)"},
383         new String[] {"Ed", "13\u65E5(\u6C34)"},
384         new String[] {"jmmssSSS", "23:58:59.123"},
385         new String[] {"JJmm", "23:58"},
386 
387         new ULocale("ja@calendar=japanese"), // (new locale for testing ticket 6872<-5702)
388         new String[] {"yM", "\u5E73\u621011/1"},
389         new String[] {"yMMM", "\u5E73\u621011\u5E741\u6708"},
390         new String[] {"yMd", "\u5E73\u621011/1/13"},
391         new String[] {"yMMMd", "\u5E73\u621011\u5E741\u670813\u65E5"},
392         new String[] {"Md", "1/13"},
393         new String[] {"MMMd", "1\u670813\u65E5"},
394         new String[] {"MMMMd", "1\u670813\u65E5"},
395         new String[] {"yQQQ", "\u5E73\u621011/Q1"},
396         new String[] {"hhmm", "\u5348\u5F8C11:58"},
397         new String[] {"HHmm", "23:58"},
398         new String[] {"jjmm", "23:58"},
399         new String[] {"mmss", "58:59"},
400         new String[] {"yyyyMMMM", "\u5E73\u621011\u5E741\u6708"},
401         new String[] {"MMMEd", "1\u670813\u65E5(\u6C34)"},
402         new String[] {"Ed", "13\u65E5(\u6C34)"},
403         new String[] {"jmmssSSS", "23:58:59.123"},
404         new String[] {"JJmm", "23:58"},
405 
406         new ULocale("zh_Hans_CN"),
407         new String[] {"yM", "1999\u5E741\u6708"},
408         new String[] {"yMMM", "1999\u5E741\u6708"}, // (fixed expected result per ticket 6872<-6626)
409         new String[] {"yMd", "1999/1/13"},
410         new String[] {"yMMMd", "1999\u5E741\u670813\u65E5"}, // (fixed expected result per ticket 6872<-6626)
411         new String[] {"Md", "1/13"},
412         new String[] {"MMMd", "1\u670813\u65E5"}, // (fixed expected result per ticket 6872<-6626)
413         new String[] {"MMMMd", "1\u670813\u65E5"},
414         new String[] {"yQQQ", "1999\u5E74\u7B2C1\u5B63\u5EA6"},
415         new String[] {"hhmm", "\u4E0B\u534811:58"},
416         new String[] {"HHmm", "23:58"},
417         new String[] {"jjmm", "\u4E0B\u534811:58"},
418         new String[] {"mmss", "58:59"},
419         new String[] {"yyyyMMMM", "1999\u5E741\u6708"}, // (new item for testing 6872<-5702)
420         new String[] {"MMMEd", "1\u670813\u65E5\u5468\u4E09"},
421         new String[] {"Ed", "13\u65E5\u5468\u4E09"},
422         new String[] {"jmmssSSS", "\u4E0B\u534811:58:59.123"},
423         new String[] {"JJmm", "11:58"},
424 
425         new ULocale("zh_TW@calendar=roc"), // (new locale for testing ticket 6872<-5702)
426         new String[] {"yM", "\u6C11\u570B88/1"},
427         new String[] {"yMMM", "\u6C11\u570B88\u5E741\u6708"},
428         new String[] {"yMd", "\u6C11\u570B88/1/13"},
429         new String[] {"yMMMd", "\u6C11\u570B88\u5E741\u670813\u65E5"},
430         new String[] {"Md", "1/13"},
431         new String[] {"MMMd", "1\u670813\u65E5"},
432         new String[] {"MMMMd", "1\u670813\u65E5"},
433         new String[] {"yQQQ", "\u6C11\u570B88\u5E741\u5B63"},
434         new String[] {"hhmm", "\u4E0B\u534811:58"},
435         new String[] {"HHmm", "23:58"},
436         new String[] {"jjmm", "\u4E0B\u534811:58"},
437         new String[] {"mmss", "58:59"},
438         new String[] {"yyyyMMMM", "\u6C11\u570B88\u5E741\u6708"},
439         new String[] {"MMMEd", "1\u670813\u65E5\u9031\u4E09"},
440         new String[] {"Ed", "13\u65E5\uFF08\u9031\u4E09\uFF09"},
441         new String[] {"jmmssSSS", "\u4E0B\u534811:58:59.123"},
442         new String[] {"JJmm", "11:58"},
443 
444         new ULocale("ru"),
445         new String[] {"yM", "01.1999"},
446         new String[] {"yMMM", "\u044F\u043D\u0432. 1999"},
447         new String[] {"yMd", "13.01.1999"},
448         new String[] {"yMMMd", "13 \u044F\u043D\u0432. 1999 \u0433."},
449         new String[] {"Md", "13.01"},
450         new String[] {"MMMd", "13 \u044F\u043D\u0432."},
451         new String[] {"MMMMd", "13 \u044F\u043D\u0432\u0430\u0440\u044F"},
452         new String[] {"yQQQ", "1-\u0439 \u043A\u0432. 1999 \u0433."},
453         new String[] {"hhmm", "11:58 PM"},
454         new String[] {"HHmm", "23:58"},
455         new String[] {"jjmm", "23:58"},
456         new String[] {"mmss", "58:59"},
457         new String[] {"yyyyMMMM", "\u044F\u043D\u0432\u0430\u0440\u044C 1999"},
458         new String[] {"MMMEd", "\u0421\u0440, 13 \u044F\u043D\u0432."},
459         new String[] {"Ed", "\u0421\u0440, 13"},
460         new String[] {"jmmssSSS", "23:58:59,123"},
461         new String[] {"JJmm", "23:58"},
462 
463         new ULocale("zh@calendar=chinese"),
464         new String[] {"yM", "1998\u620A\u5BC5\u5E74\u51AC\u6708"},
465         new String[] {"yMMM", "1998\u620A\u5BC5\u5E74\u51AC\u6708"},
466         new String[] {"yMd", "1998\u5E74\u51AC\u670826"},
467         new String[] {"yMMMd", "1998\u5E74\u51AC\u670826"},
468         new String[] {"Md", "11-26"},
469         new String[] {"MMMd", "\u51AC\u670826\u65E5"},
470         new String[] {"MMMMd", "\u51AC\u670826\u65E5"},
471         new String[] {"yQQQ", "1998\u620A\u5BC5\u5E74\u7B2C\u56DB\u5B63\u5EA6"},
472         new String[] {"hhmm", "\u4E0B\u534811:58"},
473         new String[] {"HHmm", "23:58"},
474         new String[] {"jjmm", "\u4E0B\u534811:58"},
475         new String[] {"mmss", "58:59"},
476         new String[] {"yyyyMMMM", "1998\u620A\u5BC5\u5E74\u51AC\u6708"},
477         new String[] {"MMMEd", "\u51AC\u670826\u65E5\u5468\u4E09"},
478         new String[] {"Ed", "26\u65E5\u5468\u4E09"},
479         new String[] {"jmmssSSS", "\u4E0B\u534811:58:59.123"},
480         new String[] {"JJmm", "11:58"},
481     };
482 
DayMonthTest()483     public void DayMonthTest() {
484         final ULocale locale = ULocale.FRANCE;
485 
486         // set up the generator
487         DateTimePatternGenerator dtpgen
488           = DateTimePatternGenerator.getInstance(locale);
489 
490         // get a pattern for an abbreviated month and day
491         final String pattern = dtpgen.getBestPattern("MMMd");
492         SimpleDateFormat formatter = new SimpleDateFormat(pattern, locale);
493 
494         // use it to format (or parse)
495         String formatted = formatter.format(new Date());
496         logln("formatted=" + formatted);
497         // for French, the result is "13 sept."
498     }
499 
TestOrdering()500     public void TestOrdering() {
501         ULocale[] locales = ULocale.getAvailableLocales();
502         for (int i = 0; i < locales.length; ++i) {
503             for (int style1 = DateFormat.FULL; style1 <= DateFormat.SHORT; ++style1) {
504                 for (int style2 = DateFormat.FULL; style2 < style1; ++style2) {
505                     checkCompatible(style1, style2, locales[i]);
506                 }
507             }
508         }
509     }
510 
TestReplacingZoneString()511     public void TestReplacingZoneString() {
512         Date testDate = new Date();
513         TimeZone testTimeZone = TimeZone.getTimeZone("America/New_York");
514         TimeZone bogusTimeZone = new SimpleTimeZone(1234, "Etc/Unknown");
515         Calendar calendar = Calendar.getInstance();
516         ParsePosition parsePosition = new ParsePosition(0);
517 
518         ULocale[] locales = ULocale.getAvailableLocales();
519         int count = 0;
520         for (int i = 0; i < locales.length; ++i) {
521             // skip the country locales unless we are doing exhaustive tests
522             if (getInclusion() < 6) {
523                 if (locales[i].getCountry().length() > 0) {
524                     continue;
525                 }
526             }
527             count++;
528             // Skipping some test case in the non-exhaustive mode to reduce the test time
529             //ticket#6503
530             if(params.inclusion<=5 && count%3!=0){
531                 continue;
532             }
533             logln(locales[i].toString());
534             DateTimePatternGenerator dtpgen
535             = DateTimePatternGenerator.getInstance(locales[i]);
536 
537             for (int style1 = DateFormat.FULL; style1 <= DateFormat.SHORT; ++style1) {
538                 final SimpleDateFormat oldFormat = (SimpleDateFormat) DateFormat.getTimeInstance(style1, locales[i]);
539                 String pattern = oldFormat.toPattern();
540                 String newPattern = dtpgen.replaceFieldTypes(pattern, "VVVV"); // replaceZoneString(pattern, "VVVV");
541                 if (newPattern.equals(pattern)) {
542                     continue;
543                 }
544                 // verify that it roundtrips parsing
545                 SimpleDateFormat newFormat = new SimpleDateFormat(newPattern, locales[i]);
546                 newFormat.setTimeZone(testTimeZone);
547                 String formatted = newFormat.format(testDate);
548                 calendar.setTimeZone(bogusTimeZone);
549                 parsePosition.setIndex(0);
550                 newFormat.parse(formatted, calendar, parsePosition);
551                 if (parsePosition.getErrorIndex() >= 0) {
552                     errln("Failed parse with VVVV:\t" + locales[i] + ",\t\"" + pattern + "\",\t\"" + newPattern + "\",\t\"" + formatted.substring(0,parsePosition.getErrorIndex()) + "{}" + formatted.substring(parsePosition.getErrorIndex()) + "\"");
553                 } else if (!calendar.getTimeZone().getID().equals(testTimeZone.getID())) {
554                     errln("Failed timezone roundtrip with VVVV:\t" + locales[i] + ",\t\"" + pattern + "\",\t\"" + newPattern + "\",\t\"" + formatted + "\",\t" + calendar.getTimeZone().getID() + " != " + testTimeZone.getID());
555                 } else {
556                     logln(locales[i] + ":\t\"" + pattern + "\" => \t\"" + newPattern + "\"\t" + formatted);
557                 }
558             }
559         }
560     }
561 
TestVariableCharacters()562     public void TestVariableCharacters() {
563         UnicodeSet valid = new UnicodeSet("[G y Y u U r Q q M L l w W d D F g E e c a h H K k m s S A z Z O v V X x]");
564         for (char c = 0; c < 0xFF; ++c) {
565             boolean works = false;
566             try {
567                 VariableField vf = new VariableField(String.valueOf(c), true);
568                 logln("VariableField " + vf.toString());
569                 works = true;
570             } catch (Exception e) {}
571             if (works != valid.contains(c)) {
572                 if (works) {
573                     errln("VariableField can be created with illegal character: " + c);
574                 } else {
575                     errln("VariableField can't be created with legal character: " + c);
576                 }
577             }
578         }
579     }
580 
581     static String[] DATE_STYLE_NAMES = {
582         "FULL", "LONG", "MEDIUM", "SHORT"
583     };
584 
585     /**
586      * @param fullOrder
587      * @param longOrder
588      */
checkCompatible(int style1, int style2, ULocale uLocale)589     private void checkCompatible(int style1, int style2, ULocale uLocale) {
590         DateOrder order1 = getOrdering(style1, uLocale);
591         DateOrder order2 = getOrdering(style2, uLocale);
592         if (!order1.hasSameOrderAs(order2)) {
593             // Note: This test case was updated by #6806 and no longer reports
594             // ordering difference as an error case.
595             logln(showOrderComparison(uLocale, style1, style2, order1, order2));
596         }
597     }
598 
showOrderComparison(ULocale uLocale, int style1, int style2, DateOrder order1, DateOrder order2)599     private String showOrderComparison(ULocale uLocale, int style1, int style2, DateOrder order1, DateOrder order2) {
600         String pattern1 = ((SimpleDateFormat) DateFormat.getDateInstance(style1, uLocale)).toPattern();
601         String pattern2 = ((SimpleDateFormat) DateFormat.getDateInstance(style2, uLocale)).toPattern();
602         return "Mismatch in in ordering for " + uLocale + ": " + DATE_STYLE_NAMES[style1] + ": " + order1 + ", <" + pattern1
603                 + ">; "
604                 + DATE_STYLE_NAMES[style2] + ": " + order2 + ", <" + pattern2 + ">; " ;
605     }
606 
607     /**
608      * Main date fields -- Poor-man's enum -- change to real enum when we get JDK 1.5
609      */
610     public static class DateFieldType {
611         private String name;
DateFieldType(String string)612         private DateFieldType(String string) {
613             name = string;
614         }
615 
616         public static DateFieldType
617         YEAR = new DateFieldType("YEAR"),
618         MONTH = new DateFieldType("MONTH"),
619         DAY = new DateFieldType("DAY");
620 
toString()621         public String toString() {
622             return name;
623         }
624     }
625 
626     /**
627      * Simple struct for output from getOrdering
628      */
629     static class DateOrder {
630         int monthLength;
631         DateFieldType[] fields = new DateFieldType[3];
632 
isCompatible(DateOrder other)633         public boolean isCompatible(DateOrder other) {
634             return monthLength == other.monthLength;
635         }
636         /**
637          * @param order2
638          * @return
639          */
hasSameOrderAs(DateOrder other)640         public boolean hasSameOrderAs(DateOrder other) {
641             // TODO Auto-generated method stub
642             return fields[0] == other.fields[0] && fields[1] == other.fields[1] && fields[2] == other.fields[2];
643         }
toString()644         public String toString() {
645             return "{" + monthLength + ", " + fields[0]  + ", " + fields[1]  + ", " + fields[2] + "}";
646         }
equals(Object that)647         public boolean equals(Object that) {
648             DateOrder other = (DateOrder) that;
649             return monthLength == other.monthLength && fields[0] == other.fields[0] && fields[1] == other.fields[1] && fields[2] == other.fields[2];
650         }
651     }
652 
653     DateTimePatternGenerator.FormatParser formatParser = new DateTimePatternGenerator.FormatParser ();
654     DateTimePatternGenerator generator = DateTimePatternGenerator.getEmptyInstance();
655 
656     private Calendar sampleCalendar;
657     {
658         sampleCalendar = new GregorianCalendar(TimeZone.getTimeZone("America/Los_Angeles"));
659         sampleCalendar.set(1999, Calendar.OCTOBER, 13, 23, 58, 59);
660     }
661 
662     private Date sampleDate = sampleCalendar.getTime();
663     private TimeZone gmt = TimeZone.getTimeZone("Etc/GMT");
664 
665     /**
666      * Replace the zone string with a different type, eg v's for z's, etc. <p>Called with a pattern, such as one gotten from
667      * <pre>
668      * String pattern = ((SimpleDateFormat) DateFormat.getTimeInstance(style, locale)).toPattern();
669      * </pre>
670      * @param pattern original pattern to change, such as "HH:mm zzzz"
671      * @param newZone Must be: z, zzzz, Z, ZZZZ, v, vvvv, V, or VVVV
672      * @return
673      */
replaceZoneString(String pattern, String newZone)674     public String replaceZoneString(String pattern, String newZone) {
675         final List itemList = formatParser.set(pattern).getItems();
676         boolean changed = false;
677         for (int i = 0; i < itemList.size(); ++i) {
678             Object item = itemList.get(i);
679             if (item instanceof VariableField) {
680                 VariableField variableField = (VariableField) item;
681                 if (variableField.getType() == DateTimePatternGenerator.ZONE) {
682                     if (!variableField.toString().equals(newZone)) {
683                         changed = true;
684                         itemList.set(i, new VariableField(newZone, true));
685                     }
686                 }
687             }
688         }
689         return changed ? formatParser.toString() : pattern;
690     }
691 
containsZone(String pattern)692     public boolean containsZone(String pattern) {
693         for (Iterator it = formatParser.set(pattern).getItems().iterator(); it.hasNext();) {
694             Object item = it.next();
695             if (item instanceof VariableField) {
696                 VariableField variableField = (VariableField) item;
697                 if (variableField.getType() == DateTimePatternGenerator.ZONE) {
698                     return true;
699                 }
700             }
701         }
702         return false;
703     }
704 
705     /**
706      * Get the ordering from a particular date format. Best is to use
707      * DateFormat.FULL to get the format with String form month (like "January")
708      * and DateFormat.SHORT for the numeric format order. They may be different.
709      * (Theoretically all 4 formats could be different but that never happens in
710      * practice.)
711      *
712      * @param style
713      *          DateFormat.FULL..DateFormat.SHORT
714      * @param locale
715      *          desired locale.
716      * @return
717      * @return list of ordered items DateFieldType (I
718      *         didn't know what form you really wanted so this is just a
719      *         stand-in.)
720      */
getOrdering(int style, ULocale locale)721   private DateOrder getOrdering(int style, ULocale locale) {
722       // and the date pattern
723       String pattern = ((SimpleDateFormat) DateFormat.getDateInstance(style, locale)).toPattern();
724       int count = 0;
725       DateOrder result = new DateOrder();
726 
727       for (Iterator it = formatParser.set(pattern).getItems().iterator(); it.hasNext();) {
728           Object item = it.next();
729         if (!(item instanceof String)) {
730           // the first character of the variable field determines the type,
731           // according to CLDR.
732           String variableField = item.toString();
733           switch (variableField.charAt(0)) {
734             case 'y': case 'Y': case 'u':
735               result.fields[count++] = DateFieldType.YEAR;
736               break;
737             case 'M': case 'L':
738                 result.monthLength = variableField.length();
739                 if (result.monthLength < 2) {
740                     result.monthLength = 2;
741                 }
742                 result.fields[count++] = DateFieldType.MONTH;
743               break;
744             case 'd': case 'D': case 'F': case 'g':
745                 result.fields[count++] = DateFieldType.DAY;
746               break;
747           }
748         }
749       }
750       return result;
751     }
752       /* Tests the method
753        *        public static DateTimePatternGenerator getInstance()
754        */
TestGetInstance()755       public void TestGetInstance(){
756           try{
757               DateTimePatternGenerator.getInstance();
758           } catch(Exception e){
759               errln("DateTimePatternGenerator.getInstance() was not suppose to " +
760                       "return an exception.");
761           }
762       }
763 
764       /* Tests the method
765        *        public String getSkeleton(String pattern)
766        */
TestGetSkeleton()767       public void TestGetSkeleton(){
768           DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
769           String[] cases = {"MMDD","MMMDD","MMM-DD","DD/MMM","ddM","MMMMd"};
770           String[] results = {"MMDD","MMMDD","MMMDD","MMMDD","Mdd","MMMMd"};
771           for(int i=0; i<cases.length; i++){
772               if(!dtpg.getSkeleton(cases[i]).equals(results[i])){
773                   errln("DateTimePatternGenerator.getSkeleton(String) did " +
774                           "return the expected result when passing " + cases[i] +
775                           " and expected " + results[i] + " but got " +
776                           dtpg.getSkeleton(cases[i]));
777               }
778           }
779       }
780 
781       /* Tests the method
782        *        public String getBaseSkeleton(String pattern)
783        */
TestGetBaseSkeleton()784       public void TestGetBaseSkeleton(){
785           DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
786           String[] cases = {"MMDD","MMMDD","MMM-DD","DD/MMM","ddM","MMMMd"};
787           String[] results = {"MD","MMMD","MMMD","MMMD","Md","MMMMd"};
788           for(int i=0; i<cases.length; i++){
789               if(!dtpg.getBaseSkeleton(cases[i]).equals(results[i])){
790                   errln("DateTimePatternGenerator.getSkeleton(String) did " +
791                           "return the expected result when passing " + cases[i] +
792                           " and expected " + results[i] + " but got " +
793                           dtpg.getBaseSkeleton(cases[i]));
794               }
795           }
796       }
797 
798       /* Tests the method
799        *        public Map<String, String> getSkeletons(Map<String, String> result)
800        */
TestGetSkeletons()801       public void TestGetSkeletons(){
802           DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
803           // Tests when "if (result == null)" is true
804           try{
805               dtpg.getSkeletons(null);
806           } catch(Exception e){
807               errln("DateTimePatternGenerator.getSkeletons(Map) was suppose to " +
808                       "return a new LinkedHashMap for a null parameter.");
809           }
810 
811           // Tests when "if (result == null)" is false
812           Map<String,String> mm = new LinkedHashMap<String, String>();
813           try{
814               dtpg.getSkeletons(mm);
815           } catch(Exception e){
816               errln("DateTimePatternGenerator.getSkeletons(Map) was suppose to " +
817                       "return a new LinkedHashMap for a LinkedHashMap parameter.");
818           }
819       }
820 
821       /* Tests the method
822        *        public Set<String> getBaseSkeletons(Set<String> result)
823        */
TestGetBaseSkeletons()824       public void TestGetBaseSkeletons(){
825           DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
826           // Tests when "if (result == null)" is true
827           try{
828               dtpg.getBaseSkeletons(null);
829           } catch(Exception e){
830               errln("DateTimePatternGenerator.getBaseSkeletons(Map) was suppose to " +
831                       "return a new LinkedHashMap for a null parameter.");
832           }
833 
834           // Tests when "if (result == null)" is false
835           Set<String> mm = new HashSet<String>();
836           try{
837               dtpg.getBaseSkeletons(mm);
838           } catch(Exception e){
839               errln("DateTimePatternGenerator.getBaseSkeletons(Map) was suppose to " +
840                       "return a new LinkedHashMap for a HashSet parameter.");
841           }
842       }
843 
844       /* Tests the method
845        *        public String getDecimal()
846        */
TestGetDecimal()847       public void TestGetDecimal(){
848           DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
849           if(!dtpg.getDecimal().equals(".")){
850               errln("DateTimePatternGenerator.getDecimal() was to return '.' " +
851                       "when the object gets a new instance.");
852           }
853 
854           String[] cases = {",","-","","*","&","a","0"};
855           for(int i=0; i<cases.length; i++){
856               dtpg.setDecimal(cases[i]);
857               if(!dtpg.getDecimal().equals(cases[i])){
858                   errln("DateTimePatternGenerator.getDecimal() was to return " + cases[i] +
859                           "when setting decimal with " + cases[i]);
860               }
861           }
862       }
863 
864       /* Tests the method
865        *        public Collection<String> getRedundants(Collection<String> output)
866        */
TestGetRedundants()867       public void TestGetRedundants(){
868           DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
869 
870           // Tests when "if (output == null)" is true
871           try{
872               dtpg.getRedundants(null);
873           } catch(Exception e){
874               errln("DateTimeGenerator.getRedundants was not suppose to return " +
875                       "an exception when passing a null parameter.");
876           }
877 
878           // Tests when "if (output == null)" is false
879           try{
880               Collection<String> out = new LinkedHashSet<String>();
881               dtpg.getRedundants(out);
882           } catch(Exception e){
883               errln("DateTimeGenerator.getRedundants was not suppose to return " +
884                   "an exception when passing a new LinkedHashSet<String>() parameter.");
885           }
886 
887       }
888 
889       /* Tests the method
890        *        public String getAppendItemFormat(int field)
891        */
TestGetAppendItemFormat()892       public void TestGetAppendItemFormat(){
893           DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
894           String[] cases = {"d","u","m","m","y"};
895           for(int i=0; i<cases.length; i++){
896               dtpg.setAppendItemFormat(i, cases[i]);
897               if(!dtpg.getAppendItemFormat(i).equals(cases[i])){
898                   errln("DateTimePatternGeneratorgetAppendItemFormat(int field) " +
899                           "did not return as expected. Value set at " + i + " was " +
900                           cases[i] + " but got back " + dtpg.getAppendItemFormat(i));
901               }
902           }
903       }
904 
905       /* Tests the method
906        *    public String getAppendItemName(int field)
907        */
908       private final class AppendItemName {
909           public int field;
910           public String name;
AppendItemName(int f, String n)911           public AppendItemName(int f, String n) {
912               field = f;
913               name = n;
914           }
915       }
916 
TestGetAppendItemName()917       public void TestGetAppendItemName(){
918           final AppendItemName[] appendItemNames = {
919               new AppendItemName( DateTimePatternGenerator.YEAR,    "vuosi" ),
920               new AppendItemName( DateTimePatternGenerator.MONTH,   "kuukausi" ),
921               new AppendItemName( DateTimePatternGenerator.WEEKDAY, "viikonp\u00E4iv\u00E4" ),
922               new AppendItemName( DateTimePatternGenerator.DAY,     "p\u00E4iv\u00E4" ),
923               new AppendItemName( DateTimePatternGenerator.HOUR,    "tunti" ),
924           };
925 
926           DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
927           String[] cases = {"d","u","m","m","y"};
928           for(int i=0; i<cases.length; i++){
929               dtpg.setAppendItemName(i, cases[i]);
930               if(!dtpg.getAppendItemName(i).equals(cases[i])){
931                   errln("DateTimePatternGenerator.getAppendItemFormat(int field) " +
932                           "did not return as expected. Value set at " + i + " was " +
933                           cases[i] + " but got back " + dtpg.getAppendItemName(i));
934               }
935           }
936 
937           DateTimePatternGenerator dtpgfi = DateTimePatternGenerator.getInstance(ULocale.forLanguageTag("fi"));
938           for (AppendItemName appendItemName: appendItemNames) {
939               String name = dtpgfi.getAppendItemName(appendItemName.field);
940               if (!name.equals(appendItemName.name)) {
941                   errln("DateTimePatternGenerator.getAppendItemName returns invalid name for field " + appendItemName.field);
942               }
943           }
944       }
945 
946       /* Tests the method
947        *    public static boolean isSingleField(String skeleton)
948        */
949       @SuppressWarnings("static-access")
TestIsSingleField()950     public void TestIsSingleField(){
951           DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
952           String[] cases = {" ", "m","mm","md","mmd","mmdd"};
953           boolean[] results = {true,true,true,false,false,false};
954           for(int i=0; i<cases.length; i++){
955               if(dtpg.isSingleField(cases[i]) != results[i]){
956                   errln("DateTimePatternGenerator.isSingleField(String skeleton) " +
957                           "did not return as expected. Value passed was " + cases[i] +
958                           " but got back " + dtpg.isSingleField(cases[i]));
959               }
960           }
961       }
962 
963       /* Tests the method
964        *    public Object freeze()
965        *    public Object cloneAsThawed()
966        */
TestFreezeAndCloneAsThawed()967       public void TestFreezeAndCloneAsThawed(){
968           DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
969 
970           if(dtpg.isFrozen() != false){
971               errln("DateTimePatternGenerator.isFrozen() is suppose to return false " +
972                       "for a DateTimePatternGenerator object that was just " +
973                       "created.");
974           }
975 
976           dtpg.freeze();
977           if(dtpg.isFrozen() != true){
978               errln("DateTimePatternGenerator.isFrozen() is suppose to return true " +
979                       "for a DateTimePatternGenerator object that was just " +
980                       "created and freeze.");
981           }
982 
983           DateTimePatternGenerator dtpg2 = (DateTimePatternGenerator) dtpg.cloneAsThawed();
984           if(dtpg.isFrozen() != false){
985               errln("DateTimePatternGenerator.isFrozen() is suppose to return false " +
986                       "for a DateTimePatternGenerator object that was just " +
987                       "clone as thawed.");
988           }
989           if(dtpg2.isFrozen() != false){
990               errln("DateTimePatternGenerator.isFrozen() is suppose to return false " +
991                       "for a second DateTimePatternGenerator object that was just " +
992                       "clone as thawed.");
993           }
994       }
995 
996       /* Tests the method
997        *    public Object clone()
998        */
TestClone()999       public void TestClone(){
1000           DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
1001           DateTimePatternGenerator dtpg2 = (DateTimePatternGenerator) dtpg.clone();
1002           dtpg = (DateTimePatternGenerator) dtpg2.clone();
1003       }
1004 
1005       /* Tests the constructor
1006        *    public VariableField(String string)
1007        */
1008       @SuppressWarnings("unused")
TestVariableField_String()1009       public void TestVariableField_String(){
1010           String[] cases = {"d","mm","aa"};
1011           String[] invalid = {null,"","dummy"};
1012           for(int i=0; i<cases.length; i++){
1013               try{
1014                   VariableField vf = new VariableField(cases[i]);
1015               } catch(Exception e){
1016                   errln("VariableField constructor was not suppose to return " +
1017                   "an exception when created when passing " + cases[i]);
1018               }
1019           }
1020           for(int i=0; i<invalid.length; i++){
1021               try{
1022                 VariableField vf = new VariableField(invalid[i]);
1023                   errln("VariableField constructor was suppose to return " +
1024                           "an exception when created when passing " + invalid[i]);
1025               } catch(Exception e){}
1026           }
1027       }
1028 
1029       /* Tests the method
1030        *    public FormatParser set(String string, boolean strict)
1031        */
TestSet()1032       public void TestSet(){
1033           FormatParser fp = new FormatParser();
1034           //Tests when "if (string.length() == 0)" is true
1035           try{
1036               fp.set("",true);
1037           }catch(Exception e){
1038               errln("FormatParser.set(String,boolean) was not suppose to " +
1039                       "return an exception.");
1040           }
1041       }
1042 
1043       /* Tests the method
1044        *    public String toString()
1045        */
TestToString()1046       public void TestToString(){
1047           FormatParser fp = new FormatParser();
1048           if(!fp.toString().equals("")){
1049               errln("FormatParser.toString() was suppose to return an " +
1050                       "empty string for a new FormatParser object.");
1051           }
1052 
1053           String[] cases = {"m","d","y","mm","mmm","mm dd","mm':'dd","mm-dd-yyyy"};
1054           String[] results = {"m","d","y","mm","mmm","mm dd","mm:dd","mm-dd-yyyy"};
1055           for(int i=0; i<cases.length; i++){
1056               fp.set(cases[i]);
1057               if(!fp.toString().equals(results[i])){
1058                   errln("FormatParser.toString() was suppose to return " + results[i] +
1059                   " after setting the object. Got: " + fp.toString());
1060               }
1061           }
1062       }
1063 
1064       /* Tests the method
1065        *    public boolean hasDateAndTimeFields()
1066        */
TestHasDateAndTimeFields()1067       public void TestHasDateAndTimeFields(){
1068           FormatParser fp = new FormatParser();
1069           if(fp.hasDateAndTimeFields() != false){
1070               errln("FormatParser.hasDateAndTimeFields() was suppose to return " +
1071                       "false when a new object is created.");
1072           }
1073 
1074           String[] cases = {"MMDDYY", "HHMMSS", "", "MM/DD/YYYY HH:MM:SS",
1075                   "MMDDYY HHMMSS", "HHMMSS MMDDYYYY", "HMS MDY"};
1076           boolean[] results = {false,true,false,true,true,true,true};
1077           for(int i=0; i<cases.length; i++){
1078               fp.set(cases[i]);
1079               if(fp.hasDateAndTimeFields() != results[i]){
1080                   errln("FormatParser.hasDateAndTimeFields() was suppose to " +
1081                           "return " + results[i] + " but returned " +
1082                           fp.hasDateAndTimeFields() + " for parameter " +
1083                           cases[i] + " that is set to FormatParser.");
1084               }
1085           }
1086       }
1087 
1088       /* Tests the method
1089        *    private void checkFrozen()
1090        * from public void setDateTimeFormat(String dateTimeFormat)
1091        */
TestCheckFrozen()1092       public void TestCheckFrozen(){
1093           // Tests when "if (isFrozen())" is true
1094           DateTimePatternGenerator dt = DateTimePatternGenerator.getInstance();
1095           try{
1096               dt.freeze();
1097               dt.setDateTimeFormat("MMDDYYYY");
1098               errln("DateTimePatternGenerator.checkFrozen() was suppose to " +
1099                       "return an exception when trying to setDateTimeFormat " +
1100                       "for a frozen object.");
1101           } catch(Exception e){}
1102           dt = (DateTimePatternGenerator) dt.cloneAsThawed();
1103       }
1104 
1105       /* Tests the method
1106        *    public String getFields(String pattern)
1107        */
TestGetFields()1108       public void TestGetFields(){
1109           DateTimePatternGenerator dt = DateTimePatternGenerator.getInstance();
1110           String[] cases = {"MMDDYY", "HHMMSS", "", "MM/DD/YYYY HH:MM:SS",
1111                   "MMDDYY HHMMSS", "HHMMSS MMDDYYYY", "HMS MDY"};
1112           String[] results = {"{Month:N}{Day_Of_Year:N}{Year:N}",
1113                   "{Hour:N}{Month:N}{Fractional_Second:N}","",
1114                   "{Month:N}/{Day_Of_Year:N}/{Year:N} {Hour:N}:{Month:N}:{Fractional_Second:N}",
1115                   "{Month:N}{Day_Of_Year:N}{Year:N} {Hour:N}{Month:N}{Fractional_Second:N}",
1116                   "{Hour:N}{Month:N}{Fractional_Second:N} {Month:N}{Day_Of_Year:N}{Year:N}",
1117                   "{Hour:N}{Month:N}{Fractional_Second:N} {Month:N}{Day_Of_Year:N}{Year:N}"};
1118           for(int i=0; i<cases.length; i++){
1119               if(!dt.getFields(cases[i]).equals(results[i])) {
1120                   errln("DateTimePatternGenerator.getFields(String) did not " +
1121                           "not return an expected result when passing " + cases[i] +
1122                           ". Got " + dt.getFields(cases[i]) + " but expected " +
1123                           results[i]);
1124               }
1125           }
1126       }
1127 
1128       /*
1129        * Test case for DateFormatPatternGenerator threading problem #7169
1130        */
TestT7169()1131       public void TestT7169() {
1132           Thread[] workers = new Thread[10];
1133           for (int i = 0 ; i < workers.length; i++) {
1134               workers[i] = new Thread(new Runnable() {
1135                   public void run() {
1136                       try {
1137                           for (int i = 0; i < 50; i++) {
1138                               DateTimePatternGenerator patternGenerator =
1139                                   DateTimePatternGenerator.getFrozenInstance(ULocale.US);
1140                               patternGenerator.getBestPattern("MMMMd");
1141                           }
1142                       } catch (Exception e) {
1143                           errln("FAIL: Caught an exception (frozen)" + e);
1144                       }
1145                       try {
1146                           for (int i = 0; i < 50; i++) {
1147                               DateTimePatternGenerator patternGenerator =
1148                                   DateTimePatternGenerator.getInstance(ULocale.US);
1149                               patternGenerator.getBestPattern("MMMMd");
1150                           }
1151                       } catch (Exception e) {
1152                           errln("FAIL: Caught an exception " + e);
1153                       }
1154                   }
1155               });
1156           }
1157           for (Thread wk : workers) {
1158               wk.start();
1159           }
1160           for (Thread wk : workers) {
1161               try {
1162                   wk.join();
1163               } catch (InterruptedException ie) {
1164 
1165               }
1166           }
1167       }
1168 
1169       /**
1170        * Test handling of options
1171        *
1172        * For reference, as of ICU 4.3.3,
1173        *  root/gregorian has
1174        *      Hm{"H:mm"}
1175        *      Hms{"H:mm:ss"}
1176        *      hm{"h:mm a"}
1177        *      hms{"h:mm:ss a"}
1178        *  en/gregorian has
1179        *      Hm{"H:mm"}
1180        *      Hms{"H:mm:ss"}
1181        *      hm{"h:mm a"}
1182        *  be/gregorian has
1183        *      HHmmss{"HH.mm.ss"}
1184        *      Hm{"HH.mm"}
1185        *      hm{"h.mm a"}
1186        *      hms{"h.mm.ss a"}
1187        */
1188       private final class TestOptionsItem {
1189           public String locale;
1190           public String skeleton;
1191           public String expectedPattern;
1192           public int options;
1193           // Simple constructor
TestOptionsItem(String loc, String skel, String expectedPat, int opts)1194           public TestOptionsItem(String loc, String skel, String expectedPat, int opts) {
1195               locale = loc;
1196               skeleton = skel;
1197               expectedPattern = expectedPat;
1198               options = opts;
1199           }
1200       }
TestOptions()1201       public void TestOptions() {
1202           final TestOptionsItem[] testOptionsData = {
1203               new TestOptionsItem( "en", "Hmm",  "HH:mm",   DateTimePatternGenerator.MATCH_NO_OPTIONS        ),
1204               new TestOptionsItem( "en", "HHmm", "HH:mm",   DateTimePatternGenerator.MATCH_NO_OPTIONS        ),
1205               new TestOptionsItem( "en", "hhmm", "h:mm a",  DateTimePatternGenerator.MATCH_NO_OPTIONS        ),
1206               new TestOptionsItem( "en", "Hmm",  "HH:mm",   DateTimePatternGenerator.MATCH_HOUR_FIELD_LENGTH ),
1207               new TestOptionsItem( "en", "HHmm", "HH:mm",   DateTimePatternGenerator.MATCH_HOUR_FIELD_LENGTH ),
1208               new TestOptionsItem( "en", "hhmm", "hh:mm a", DateTimePatternGenerator.MATCH_HOUR_FIELD_LENGTH ),
1209               new TestOptionsItem( "be", "Hmm",  "HH.mm",   DateTimePatternGenerator.MATCH_NO_OPTIONS        ),
1210               new TestOptionsItem( "be", "HHmm", "HH.mm",   DateTimePatternGenerator.MATCH_NO_OPTIONS        ),
1211               new TestOptionsItem( "be", "hhmm", "h.mm a",  DateTimePatternGenerator.MATCH_NO_OPTIONS        ),
1212               new TestOptionsItem( "be", "Hmm",  "H.mm",    DateTimePatternGenerator.MATCH_HOUR_FIELD_LENGTH ),
1213               new TestOptionsItem( "be", "HHmm", "HH.mm",   DateTimePatternGenerator.MATCH_HOUR_FIELD_LENGTH ),
1214               new TestOptionsItem( "be", "hhmm", "hh.mm a", DateTimePatternGenerator.MATCH_HOUR_FIELD_LENGTH ),
1215               //
1216               new TestOptionsItem( "en",                   "yyyy",  "yyyy",  DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1217               new TestOptionsItem( "en",                   "YYYY",  "YYYY",  DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1218               new TestOptionsItem( "en",                   "U",     "y",     DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1219               new TestOptionsItem( "en@calendar=japanese", "yyyy",  "y G",   DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1220               new TestOptionsItem( "en@calendar=japanese", "YYYY",  "Y G",   DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1221               new TestOptionsItem( "en@calendar=japanese", "U",     "y G",   DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1222               new TestOptionsItem( "en@calendar=chinese",  "yyyy",  "r(U)",     DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1223               new TestOptionsItem( "en@calendar=chinese",  "YYYY",  "Y(Y)",     DateTimePatternGenerator.MATCH_NO_OPTIONS ), // not a good result, want r(Y) or r(U)
1224               new TestOptionsItem( "en@calendar=chinese",  "U",     "r(U)",     DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1225               new TestOptionsItem( "en@calendar=chinese",  "Gy",    "r(U)",     DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1226               new TestOptionsItem( "en@calendar=chinese",  "GU",    "r(U)",     DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1227               new TestOptionsItem( "en@calendar=chinese",  "ULLL",  "MMM U",    DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1228               new TestOptionsItem( "en@calendar=chinese",  "yMMM",  "MMM r(U)", DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1229               new TestOptionsItem( "en@calendar=chinese",  "GUMMM", "MMM r(U)", DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1230               new TestOptionsItem( "zh@calendar=chinese",  "yyyy",  "rU\u5E74",    DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1231               new TestOptionsItem( "zh@calendar=chinese",  "YYYY",  "YY\u5E74",    DateTimePatternGenerator.MATCH_NO_OPTIONS ), // not a good result, want r(Y) or r(U)
1232               new TestOptionsItem( "zh@calendar=chinese",  "U",     "rU\u5E74",    DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1233               new TestOptionsItem( "zh@calendar=chinese",  "Gy",    "rU\u5E74",    DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1234               new TestOptionsItem( "zh@calendar=chinese",  "GU",    "rU\u5E74",    DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1235               new TestOptionsItem( "zh@calendar=chinese",  "ULLL",  "U\u5E74MMM",  DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1236               new TestOptionsItem( "zh@calendar=chinese",  "yMMM",  "rU\u5E74MMM", DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1237               new TestOptionsItem( "zh@calendar=chinese",  "GUMMM", "rU\u5E74MMM", DateTimePatternGenerator.MATCH_NO_OPTIONS ),
1238           };
1239 
1240           for (int i = 0; i < testOptionsData.length; ++i) {
1241               ULocale uloc = new ULocale(testOptionsData[i].locale);
1242               DateTimePatternGenerator dtpgen = DateTimePatternGenerator.getInstance(uloc);
1243               String pattern = dtpgen.getBestPattern(testOptionsData[i].skeleton, testOptionsData[i].options);
1244               if (pattern.compareTo(testOptionsData[i].expectedPattern) != 0) {
1245                   errln("Locale " + testOptionsData[i].locale + ", skeleton " + testOptionsData[i].skeleton +
1246                       ", options " + ((testOptionsData[i].options != 0)? "!=0": "==0") +
1247                       ", expected pattern " + testOptionsData[i].expectedPattern + ", got " + pattern);
1248               }
1249           }
1250       }
1251 
1252     /**
1253      * Test that DTPG can handle all valid pattern character / length combinations
1254      */
1255     private final class AllFieldsTestItem {
1256         public char patternChar;
1257         public int[] fieldLengths;
1258         public String mustIncludeOneOf;
1259         // Simple constructor
AllFieldsTestItem(char pC, int[] fL, String mI)1260         public AllFieldsTestItem(char pC, int[] fL, String mI) {
1261             patternChar = pC;
1262             fieldLengths = fL;
1263             mustIncludeOneOf = mI;
1264         }
1265     }
TestAllFieldPatterns()1266     public void TestAllFieldPatterns() {
1267         String[] localeNames = {
1268             "root",
1269             "root@calendar=japanese",
1270             "root@calendar=chinese",
1271             "en",
1272             "en@calendar=japanese",
1273             "en@calendar=chinese",
1274         };
1275         final AllFieldsTestItem[] testItems = {
1276             //                     pat   fieldLengths             generated pattern must
1277             //                     chr   to test                  include one of these
1278             new AllFieldsTestItem( 'G',  new int[]{1,2,3,4,5},    "G"    ), // era
1279             // year
1280             new AllFieldsTestItem( 'y',  new int[]{1,2,3,4},      "yU"   ), // year
1281             new AllFieldsTestItem( 'Y',  new int[]{1,2,3,4},      "Y"    ), // year for week of year
1282             new AllFieldsTestItem( 'u',  new int[]{1,2,3,4,5},    "yuU"  ), // extended year
1283             new AllFieldsTestItem( 'U',  new int[]{1,2,3,4,5},    "yU"   ), // cyclic year name
1284             // quarter
1285             new AllFieldsTestItem( 'Q',  new int[]{1,2,3,4},      "Qq"   ), // x
1286             new AllFieldsTestItem( 'q',  new int[]{1,2,3,4},      "Qq"   ), // standalone
1287             // month
1288             new AllFieldsTestItem( 'M',  new int[]{1,2,3,4,5},    "ML"   ), // x
1289             new AllFieldsTestItem( 'L',  new int[]{1,2,3,4,5},    "ML"   ), // standalone
1290             // week
1291             new AllFieldsTestItem( 'w',  new int[]{1,2},          "w"    ), // week of year
1292             new AllFieldsTestItem( 'W',  new int[]{1},            "W"    ), // week of month
1293             // day
1294             new AllFieldsTestItem( 'd',  new int[]{1,2},          "d"    ), // day of month
1295             new AllFieldsTestItem( 'D',  new int[]{1,2,3},        "D"    ), // day of year
1296             new AllFieldsTestItem( 'F',  new int[]{1},            "F"    ), // day of week in month
1297             new AllFieldsTestItem( 'g',  new int[]{7},            "g"    ), // modified julian day
1298             // weekday
1299             new AllFieldsTestItem( 'E',  new int[]{1,2,3,4,5,6},  "Eec"  ), // day of week
1300             new AllFieldsTestItem( 'e',  new int[]{1,2,3,4,5,6},  "Eec"  ), // local day of week
1301             new AllFieldsTestItem( 'c',  new int[]{1,2,3,4,5,6},  "Eec"  ), // standalone local day of week
1302             // day period
1303         //  new AllFieldsTestItem( 'a',  new int[]{1},            "a"    ), // am or pm   // not clear this one is supposed to work (it doesn't)
1304             // hour
1305             new AllFieldsTestItem( 'h',  new int[]{1,2},          "hK"   ), // 12 (1-12)
1306             new AllFieldsTestItem( 'H',  new int[]{1,2},          "Hk"   ), // 24 (0-23)
1307             new AllFieldsTestItem( 'K',  new int[]{1,2},          "hK"   ), // 12 (0-11)
1308             new AllFieldsTestItem( 'k',  new int[]{1,2},          "Hk"   ), // 24 (1-24)
1309             new AllFieldsTestItem( 'j',  new int[]{1,2},          "hHKk" ), // locale default
1310             // minute
1311             new AllFieldsTestItem( 'm',  new int[]{1,2},          "m"    ), // x
1312             // second & fractions
1313             new AllFieldsTestItem( 's',  new int[]{1,2},          "s"    ), // x
1314             new AllFieldsTestItem( 'S',  new int[]{1,2,3,4},      "S"    ), // fractional second
1315             new AllFieldsTestItem( 'A',  new int[]{8},            "A"    ), // milliseconds in day
1316             // zone
1317             new AllFieldsTestItem( 'z',  new int[]{1,2,3,4},      "z"    ), // x
1318             new AllFieldsTestItem( 'Z',  new int[]{1,2,3,4,5},    "Z"    ), // x
1319             new AllFieldsTestItem( 'O',  new int[]{1,4},          "O"    ), // x
1320             new AllFieldsTestItem( 'v',  new int[]{1,4},          "v"    ), // x
1321             new AllFieldsTestItem( 'V',  new int[]{1,2,3,4},      "V"    ), // x
1322             new AllFieldsTestItem( 'X',  new int[]{1,2,3,4,5},    "X"    ), // x
1323             new AllFieldsTestItem( 'x',  new int[]{1,2,3,4,5},    "x"    ), // x
1324         };
1325         final int FIELD_LENGTH_MAX = 8;
1326 
1327         for (String localeName: localeNames) {
1328             ULocale uloc = new ULocale(localeName);
1329             DateTimePatternGenerator dtpgen = DateTimePatternGenerator.getInstance(uloc);
1330             for (AllFieldsTestItem testItem: testItems) {
1331                 char[] skelBuf = new char[FIELD_LENGTH_MAX];
1332                 for (int chrIndx = 0; chrIndx < FIELD_LENGTH_MAX; chrIndx++) {
1333                     skelBuf[chrIndx] = testItem.patternChar;
1334                 }
1335                 for (int lenIndx = 0; lenIndx < testItem.fieldLengths.length; lenIndx++) {
1336                     int skelLen = testItem.fieldLengths[lenIndx];
1337                     if (skelLen > FIELD_LENGTH_MAX) {
1338                         continue;
1339                     };
1340                     String skeleton = new String(skelBuf, 0, skelLen);
1341                     String pattern = dtpgen.getBestPattern(skeleton);
1342                     if (pattern.length() <= 0) {
1343                         errln("DateTimePatternGenerator getBestPattern for locale " + localeName +
1344                               ", skeleton " + skeleton + ", produces 0-length pattern");
1345                     } else {
1346                         // test that resulting pattern has at least one char in mustIncludeOneOf
1347                         boolean inQuoted = false;
1348                         int patIndx, patLen = pattern.length();
1349                         for (patIndx = 0; patIndx < patLen; patIndx++) {
1350                             char c = pattern.charAt(patIndx);
1351                             if (c == '\'') {
1352                                 inQuoted = !inQuoted;
1353                             } else if (!inQuoted && c <= 'z' && c >= 'A') {
1354                                 if (testItem.mustIncludeOneOf.indexOf(c) >= 0) {
1355                                     break;
1356                                 }
1357                             }
1358                         }
1359                         if (patIndx >= patLen) {
1360                             errln("DateTimePatternGenerator getBestPattern for locale " + localeName +
1361                                   ", skeleton " + skeleton +
1362                                   ", produces pattern without required chars: " + pattern);
1363                         }
1364                     }
1365                 }
1366             }
1367         }
1368     }
1369 
TestJavaLocale()1370     public void TestJavaLocale() {
1371         DateTimePatternGenerator genUloc = DateTimePatternGenerator.getInstance(ULocale.GERMANY);
1372         DateTimePatternGenerator genLoc = DateTimePatternGenerator.getInstance(Locale.GERMANY);
1373 
1374         final String pat = "yMdHms";
1375         String patUloc = genUloc.getBestPattern(pat);
1376         String patLoc = genLoc.getBestPattern(pat);
1377 
1378         assertEquals("German pattern 'yMdHms' - getInstance with Java Locale", patUloc, patLoc);
1379     }
1380 }
1381