1 /*
2  * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /*
25  * This file is available under and governed by the GNU General Public
26  * License version 2 only, as published by the Free Software Foundation.
27  * However, the following notice accompanied the original version of this
28  * file:
29  *
30  * Copyright (c) 2009-2012, Stephen Colebourne & Michael Nascimento Santos
31  *
32  * All rights reserved.
33  *
34  * Redistribution and use in source and binary forms, with or without
35  * modification, are permitted provided that the following conditions are met:
36  *
37  *  * Redistributions of source code must retain the above copyright notice,
38  *    this list of conditions and the following disclaimer.
39  *
40  *  * Redistributions in binary form must reproduce the above copyright notice,
41  *    this list of conditions and the following disclaimer in the documentation
42  *    and/or other materials provided with the distribution.
43  *
44  *  * Neither the name of JSR-310 nor the names of its contributors
45  *    may be used to endorse or promote products derived from this software
46  *    without specific prior written permission.
47  *
48  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
49  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
50  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
51  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
52  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
53  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
54  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
55  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
56  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
57  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
58  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59  */
60 package test.java.time.format;
61 
62 import static java.time.temporal.ChronoField.DAY_OF_MONTH;
63 import static java.time.temporal.ChronoField.DAY_OF_WEEK;
64 import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
65 import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
66 import static java.time.temporal.ChronoField.YEAR;
67 import static org.testng.Assert.assertEquals;
68 import static org.testng.Assert.assertNotNull;
69 
70 import java.text.ParsePosition;
71 import java.time.LocalDate;
72 import java.time.YearMonth;
73 import java.time.ZoneOffset;
74 import java.time.chrono.Chronology;
75 import java.time.chrono.IsoChronology;
76 import java.time.chrono.JapaneseChronology;
77 import java.time.chrono.MinguoChronology;
78 import java.time.format.DateTimeFormatter;
79 import java.time.format.DateTimeFormatterBuilder;
80 import java.time.format.FormatStyle;
81 import java.time.format.SignStyle;
82 import java.time.format.TextStyle;
83 import java.time.temporal.Temporal;
84 import java.time.temporal.TemporalAccessor;
85 import java.util.HashMap;
86 import java.util.Locale;
87 import java.util.Map;
88 
89 import org.testng.annotations.BeforeMethod;
90 import org.testng.annotations.DataProvider;
91 import org.testng.annotations.Test;
92 
93 /**
94  * Test DateTimeFormatterBuilder.
95  */
96 @Test
97 public class TestDateTimeFormatterBuilder {
98 
99     private DateTimeFormatterBuilder builder;
100 
101     @BeforeMethod
setUp()102     public void setUp() {
103         builder = new DateTimeFormatterBuilder();
104     }
105 
106     //-----------------------------------------------------------------------
107     @Test
test_toFormatter_empty()108     public void test_toFormatter_empty() throws Exception {
109         DateTimeFormatter f = builder.toFormatter();
110         assertEquals(f.toString(), "");
111     }
112 
113     //-----------------------------------------------------------------------
114     @Test
test_parseCaseSensitive()115     public void test_parseCaseSensitive() throws Exception {
116         builder.parseCaseSensitive();
117         DateTimeFormatter f = builder.toFormatter();
118         assertEquals(f.toString(), "ParseCaseSensitive(true)");
119     }
120 
121     @Test
test_parseCaseInsensitive()122     public void test_parseCaseInsensitive() throws Exception {
123         builder.parseCaseInsensitive();
124         DateTimeFormatter f = builder.toFormatter();
125         assertEquals(f.toString(), "ParseCaseSensitive(false)");
126     }
127 
128     //-----------------------------------------------------------------------
129     @Test
test_parseStrict()130     public void test_parseStrict() throws Exception {
131         builder.parseStrict();
132         DateTimeFormatter f = builder.toFormatter();
133         assertEquals(f.toString(), "ParseStrict(true)");
134     }
135 
136     @Test
test_parseLenient()137     public void test_parseLenient() throws Exception {
138         builder.parseLenient();
139         DateTimeFormatter f = builder.toFormatter();
140         assertEquals(f.toString(), "ParseStrict(false)");
141     }
142 
143     //-----------------------------------------------------------------------
144     @Test
test_appendValue_1arg()145     public void test_appendValue_1arg() throws Exception {
146         builder.appendValue(DAY_OF_MONTH);
147         DateTimeFormatter f = builder.toFormatter();
148         assertEquals(f.toString(), "Value(DayOfMonth)");
149     }
150 
151     @Test(expectedExceptions=NullPointerException.class)
test_appendValue_1arg_null()152     public void test_appendValue_1arg_null() throws Exception {
153         builder.appendValue(null);
154     }
155 
156     //-----------------------------------------------------------------------
157     @Test
test_appendValue_2arg()158     public void test_appendValue_2arg() throws Exception {
159         builder.appendValue(DAY_OF_MONTH, 3);
160         DateTimeFormatter f = builder.toFormatter();
161         assertEquals(f.toString(), "Value(DayOfMonth,3)");
162     }
163 
164     @Test(expectedExceptions=NullPointerException.class)
test_appendValue_2arg_null()165     public void test_appendValue_2arg_null() throws Exception {
166         builder.appendValue(null, 3);
167     }
168 
169     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_2arg_widthTooSmall()170     public void test_appendValue_2arg_widthTooSmall() throws Exception {
171         builder.appendValue(DAY_OF_MONTH, 0);
172     }
173 
174     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_2arg_widthTooBig()175     public void test_appendValue_2arg_widthTooBig() throws Exception {
176         builder.appendValue(DAY_OF_MONTH, 20);
177     }
178 
179     //-----------------------------------------------------------------------
180     @Test
test_appendValue_3arg()181     public void test_appendValue_3arg() throws Exception {
182         builder.appendValue(DAY_OF_MONTH, 2, 3, SignStyle.NORMAL);
183         DateTimeFormatter f = builder.toFormatter();
184         assertEquals(f.toString(), "Value(DayOfMonth,2,3,NORMAL)");
185     }
186 
187     @Test(expectedExceptions=NullPointerException.class)
test_appendValue_3arg_nullField()188     public void test_appendValue_3arg_nullField() throws Exception {
189         builder.appendValue(null, 2, 3, SignStyle.NORMAL);
190     }
191 
192     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_3arg_minWidthTooSmall()193     public void test_appendValue_3arg_minWidthTooSmall() throws Exception {
194         builder.appendValue(DAY_OF_MONTH, 0, 2, SignStyle.NORMAL);
195     }
196 
197     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_3arg_minWidthTooBig()198     public void test_appendValue_3arg_minWidthTooBig() throws Exception {
199         builder.appendValue(DAY_OF_MONTH, 20, 2, SignStyle.NORMAL);
200     }
201 
202     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_3arg_maxWidthTooSmall()203     public void test_appendValue_3arg_maxWidthTooSmall() throws Exception {
204         builder.appendValue(DAY_OF_MONTH, 2, 0, SignStyle.NORMAL);
205     }
206 
207     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_3arg_maxWidthTooBig()208     public void test_appendValue_3arg_maxWidthTooBig() throws Exception {
209         builder.appendValue(DAY_OF_MONTH, 2, 20, SignStyle.NORMAL);
210     }
211 
212     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_3arg_maxWidthMinWidth()213     public void test_appendValue_3arg_maxWidthMinWidth() throws Exception {
214         builder.appendValue(DAY_OF_MONTH, 4, 2, SignStyle.NORMAL);
215     }
216 
217     @Test(expectedExceptions=NullPointerException.class)
test_appendValue_3arg_nullSignStyle()218     public void test_appendValue_3arg_nullSignStyle() throws Exception {
219         builder.appendValue(DAY_OF_MONTH, 2, 3, null);
220     }
221 
222     //-----------------------------------------------------------------------
223     @Test
test_appendValue_subsequent2_parse3()224     public void test_appendValue_subsequent2_parse3() throws Exception {
225         builder.appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL).appendValue(DAY_OF_MONTH, 2);
226         DateTimeFormatter f = builder.toFormatter();
227         assertEquals(f.toString(), "Value(MonthOfYear,1,2,NORMAL)Value(DayOfMonth,2)");
228         TemporalAccessor parsed = f.parseUnresolved("123", new ParsePosition(0));
229         assertEquals(parsed.getLong(MONTH_OF_YEAR), 1L);
230         assertEquals(parsed.getLong(DAY_OF_MONTH), 23L);
231     }
232 
233     @Test
test_appendValue_subsequent2_parse4()234     public void test_appendValue_subsequent2_parse4() throws Exception {
235         builder.appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL).appendValue(DAY_OF_MONTH, 2);
236         DateTimeFormatter f = builder.toFormatter();
237         assertEquals(f.toString(), "Value(MonthOfYear,1,2,NORMAL)Value(DayOfMonth,2)");
238         TemporalAccessor parsed = f.parseUnresolved("0123", new ParsePosition(0));
239         assertEquals(parsed.getLong(MONTH_OF_YEAR), 1L);
240         assertEquals(parsed.getLong(DAY_OF_MONTH), 23L);
241     }
242 
243     @Test
test_appendValue_subsequent2_parse5()244     public void test_appendValue_subsequent2_parse5() throws Exception {
245         builder.appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL).appendValue(DAY_OF_MONTH, 2).appendLiteral('4');
246         DateTimeFormatter f = builder.toFormatter();
247         assertEquals(f.toString(), "Value(MonthOfYear,1,2,NORMAL)Value(DayOfMonth,2)'4'");
248         TemporalAccessor parsed = f.parseUnresolved("01234", new ParsePosition(0));
249         assertEquals(parsed.getLong(MONTH_OF_YEAR), 1L);
250         assertEquals(parsed.getLong(DAY_OF_MONTH), 23L);
251     }
252 
253     @Test
test_appendValue_subsequent3_parse6()254     public void test_appendValue_subsequent3_parse6() throws Exception {
255         builder
256             .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
257             .appendValue(MONTH_OF_YEAR, 2)
258             .appendValue(DAY_OF_MONTH, 2);
259         DateTimeFormatter f = builder.toFormatter();
260         assertEquals(f.toString(), "Value(Year,4,10,EXCEEDS_PAD)Value(MonthOfYear,2)Value(DayOfMonth,2)");
261         TemporalAccessor parsed = f.parseUnresolved("20090630", new ParsePosition(0));
262         assertEquals(parsed.getLong(YEAR), 2009L);
263         assertEquals(parsed.getLong(MONTH_OF_YEAR), 6L);
264         assertEquals(parsed.getLong(DAY_OF_MONTH), 30L);
265     }
266 
267     //-----------------------------------------------------------------------
268     @Test(expectedExceptions=NullPointerException.class)
test_appendValueReduced_null()269     public void test_appendValueReduced_null() throws Exception {
270         builder.appendValueReduced(null, 2, 2, 2000);
271     }
272 
273     @Test
test_appendValueReduced()274     public void test_appendValueReduced() throws Exception {
275         builder.appendValueReduced(YEAR, 2, 2, 2000);
276         DateTimeFormatter f = builder.toFormatter();
277         assertEquals(f.toString(), "ReducedValue(Year,2,2,2000)");
278         TemporalAccessor parsed = f.parseUnresolved("12", new ParsePosition(0));
279         assertEquals(parsed.getLong(YEAR), 2012L);
280     }
281 
282     @Test
test_appendValueReduced_subsequent_parse()283     public void test_appendValueReduced_subsequent_parse() throws Exception {
284         builder.appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL).appendValueReduced(YEAR, 2, 2, 2000);
285         DateTimeFormatter f = builder.toFormatter();
286         assertEquals(f.toString(), "Value(MonthOfYear,1,2,NORMAL)ReducedValue(Year,2,2,2000)");
287         ParsePosition ppos = new ParsePosition(0);
288         TemporalAccessor parsed = f.parseUnresolved("123", ppos);
289         assertNotNull(parsed, "Parse failed: " + ppos.toString());
290         assertEquals(parsed.getLong(MONTH_OF_YEAR), 1L);
291         assertEquals(parsed.getLong(YEAR), 2023L);
292     }
293 
294     //-----------------------------------------------------------------------
295     //-----------------------------------------------------------------------
296     //-----------------------------------------------------------------------
297     @Test
test_appendFraction_4arg()298     public void test_appendFraction_4arg() throws Exception {
299         builder.appendFraction(MINUTE_OF_HOUR, 1, 9, false);
300         DateTimeFormatter f = builder.toFormatter();
301         assertEquals(f.toString(), "Fraction(MinuteOfHour,1,9)");
302     }
303 
304     @Test(expectedExceptions=NullPointerException.class)
test_appendFraction_4arg_nullRule()305     public void test_appendFraction_4arg_nullRule() throws Exception {
306         builder.appendFraction(null, 1, 9, false);
307     }
308 
309     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendFraction_4arg_invalidRuleNotFixedSet()310     public void test_appendFraction_4arg_invalidRuleNotFixedSet() throws Exception {
311         builder.appendFraction(DAY_OF_MONTH, 1, 9, false);
312     }
313 
314     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendFraction_4arg_minTooSmall()315     public void test_appendFraction_4arg_minTooSmall() throws Exception {
316         builder.appendFraction(MINUTE_OF_HOUR, -1, 9, false);
317     }
318 
319     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendFraction_4arg_minTooBig()320     public void test_appendFraction_4arg_minTooBig() throws Exception {
321         builder.appendFraction(MINUTE_OF_HOUR, 10, 9, false);
322     }
323 
324     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendFraction_4arg_maxTooSmall()325     public void test_appendFraction_4arg_maxTooSmall() throws Exception {
326         builder.appendFraction(MINUTE_OF_HOUR, 0, -1, false);
327     }
328 
329     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendFraction_4arg_maxTooBig()330     public void test_appendFraction_4arg_maxTooBig() throws Exception {
331         builder.appendFraction(MINUTE_OF_HOUR, 1, 10, false);
332     }
333 
334     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendFraction_4arg_maxWidthMinWidth()335     public void test_appendFraction_4arg_maxWidthMinWidth() throws Exception {
336         builder.appendFraction(MINUTE_OF_HOUR, 9, 3, false);
337     }
338 
339     //-----------------------------------------------------------------------
340     //-----------------------------------------------------------------------
341     //-----------------------------------------------------------------------
342     @Test
test_appendText_1arg()343     public void test_appendText_1arg() throws Exception {
344         builder.appendText(MONTH_OF_YEAR);
345         DateTimeFormatter f = builder.toFormatter();
346         assertEquals(f.toString(), "Text(MonthOfYear)");
347     }
348 
349     @Test(expectedExceptions=NullPointerException.class)
test_appendText_1arg_null()350     public void test_appendText_1arg_null() throws Exception {
351         builder.appendText(null);
352     }
353 
354     //-----------------------------------------------------------------------
355     @Test
test_appendText_2arg()356     public void test_appendText_2arg() throws Exception {
357         builder.appendText(MONTH_OF_YEAR, TextStyle.SHORT);
358         DateTimeFormatter f = builder.toFormatter();
359         assertEquals(f.toString(), "Text(MonthOfYear,SHORT)");
360     }
361 
362     @Test(expectedExceptions=NullPointerException.class)
test_appendText_2arg_nullRule()363     public void test_appendText_2arg_nullRule() throws Exception {
364         builder.appendText(null, TextStyle.SHORT);
365     }
366 
367     @Test(expectedExceptions=NullPointerException.class)
test_appendText_2arg_nullStyle()368     public void test_appendText_2arg_nullStyle() throws Exception {
369         builder.appendText(MONTH_OF_YEAR, (TextStyle) null);
370     }
371 
372     //-----------------------------------------------------------------------
373     @Test
test_appendTextMap()374     public void test_appendTextMap() throws Exception {
375         Map<Long, String> map = new HashMap<>();
376         map.put(1L, "JNY");
377         map.put(2L, "FBY");
378         map.put(3L, "MCH");
379         map.put(4L, "APL");
380         map.put(5L, "MAY");
381         map.put(6L, "JUN");
382         map.put(7L, "JLY");
383         map.put(8L, "AGT");
384         map.put(9L, "SPT");
385         map.put(10L, "OBR");
386         map.put(11L, "NVR");
387         map.put(12L, "DBR");
388         builder.appendText(MONTH_OF_YEAR, map);
389         DateTimeFormatter f = builder.toFormatter();
390         assertEquals(f.toString(), "Text(MonthOfYear)");  // TODO: toString should be different?
391     }
392 
393     @Test(expectedExceptions=NullPointerException.class)
test_appendTextMap_nullRule()394     public void test_appendTextMap_nullRule() throws Exception {
395         builder.appendText(null, new HashMap<Long, String>());
396     }
397 
398     @Test(expectedExceptions=NullPointerException.class)
test_appendTextMap_nullStyle()399     public void test_appendTextMap_nullStyle() throws Exception {
400         builder.appendText(MONTH_OF_YEAR, (Map<Long, String>) null);
401     }
402 
403     //-----------------------------------------------------------------------
404     //-----------------------------------------------------------------------
405     //-----------------------------------------------------------------------
406     @Test
test_appendOffsetId()407     public void test_appendOffsetId() throws Exception {
408         builder.appendOffsetId();
409         DateTimeFormatter f = builder.toFormatter();
410         assertEquals(f.toString(), "Offset(+HH:MM:ss,'Z')");
411     }
412 
413     @DataProvider(name="offsetPatterns")
data_offsetPatterns()414     Object[][] data_offsetPatterns() {
415         return new Object[][] {
416                 {"+HH", 2, 0, 0, "+02"},
417                 {"+HH", -2, 0, 0, "-02"},
418                 {"+HH", 2, 30, 0, "+02"},
419                 {"+HH", 2, 0, 45, "+02"},
420                 {"+HH", 2, 30, 45, "+02"},
421 
422                 {"+HHMM", 2, 0, 0, "+0200"},
423                 {"+HHMM", -2, 0, 0, "-0200"},
424                 {"+HHMM", 2, 30, 0, "+0230"},
425                 {"+HHMM", 2, 0, 45, "+0200"},
426                 {"+HHMM", 2, 30, 45, "+0230"},
427 
428                 {"+HH:MM", 2, 0, 0, "+02:00"},
429                 {"+HH:MM", -2, 0, 0, "-02:00"},
430                 {"+HH:MM", 2, 30, 0, "+02:30"},
431                 {"+HH:MM", 2, 0, 45, "+02:00"},
432                 {"+HH:MM", 2, 30, 45, "+02:30"},
433 
434                 {"+HHMMss", 2, 0, 0, "+0200"},
435                 {"+HHMMss", -2, 0, 0, "-0200"},
436                 {"+HHMMss", 2, 30, 0, "+0230"},
437                 {"+HHMMss", 2, 0, 45, "+020045"},
438                 {"+HHMMss", 2, 30, 45, "+023045"},
439 
440                 {"+HH:MM:ss", 2, 0, 0, "+02:00"},
441                 {"+HH:MM:ss", -2, 0, 0, "-02:00"},
442                 {"+HH:MM:ss", 2, 30, 0, "+02:30"},
443                 {"+HH:MM:ss", 2, 0, 45, "+02:00:45"},
444                 {"+HH:MM:ss", 2, 30, 45, "+02:30:45"},
445 
446                 {"+HHMMSS", 2, 0, 0, "+020000"},
447                 {"+HHMMSS", -2, 0, 0, "-020000"},
448                 {"+HHMMSS", 2, 30, 0, "+023000"},
449                 {"+HHMMSS", 2, 0, 45, "+020045"},
450                 {"+HHMMSS", 2, 30, 45, "+023045"},
451 
452                 {"+HH:MM:SS", 2, 0, 0, "+02:00:00"},
453                 {"+HH:MM:SS", -2, 0, 0, "-02:00:00"},
454                 {"+HH:MM:SS", 2, 30, 0, "+02:30:00"},
455                 {"+HH:MM:SS", 2, 0, 45, "+02:00:45"},
456                 {"+HH:MM:SS", 2, 30, 45, "+02:30:45"},
457         };
458     }
459 
460     @Test(dataProvider="offsetPatterns")
test_appendOffset_format(String pattern, int h, int m, int s, String expected)461     public void test_appendOffset_format(String pattern, int h, int m, int s, String expected) throws Exception {
462         builder.appendOffset(pattern, "Z");
463         DateTimeFormatter f = builder.toFormatter();
464         ZoneOffset offset = ZoneOffset.ofHoursMinutesSeconds(h, m, s);
465         assertEquals(f.format(offset), expected);
466     }
467 
468     @Test(dataProvider="offsetPatterns")
test_appendOffset_parse(String pattern, int h, int m, int s, String expected)469     public void test_appendOffset_parse(String pattern, int h, int m, int s, String expected) throws Exception {
470         builder.appendOffset(pattern, "Z");
471         DateTimeFormatter f = builder.toFormatter();
472         ZoneOffset offset = ZoneOffset.ofHoursMinutesSeconds(h, m, s);
473         ZoneOffset parsed = f.parse(expected, ZoneOffset::from);
474         assertEquals(f.format(parsed), expected);
475     }
476 
477     @DataProvider(name="badOffsetPatterns")
data_badOffsetPatterns()478     Object[][] data_badOffsetPatterns() {
479         return new Object[][] {
480             {"HH"},
481             {"HHMM"},
482             {"HH:MM"},
483             {"HHMMss"},
484             {"HH:MM:ss"},
485             {"HHMMSS"},
486             {"HH:MM:SS"},
487             {"+H"},
488             {"+HMM"},
489             {"+HHM"},
490             {"+A"},
491         };
492     }
493 
494     @Test(dataProvider="badOffsetPatterns", expectedExceptions=IllegalArgumentException.class)
test_appendOffset_badPattern(String pattern)495     public void test_appendOffset_badPattern(String pattern) throws Exception {
496         builder.appendOffset(pattern, "Z");
497     }
498 
499     @Test(expectedExceptions=NullPointerException.class)
test_appendOffset_3arg_nullText()500     public void test_appendOffset_3arg_nullText() throws Exception {
501         builder.appendOffset("+HH:MM", null);
502     }
503 
504     @Test(expectedExceptions=NullPointerException.class)
test_appendOffset_3arg_nullPattern()505     public void test_appendOffset_3arg_nullPattern() throws Exception {
506         builder.appendOffset(null, "Z");
507     }
508 
509     //-----------------------------------------------------------------------
510     //-----------------------------------------------------------------------
511     //-----------------------------------------------------------------------
512     @Test
test_appendZoneId()513     public void test_appendZoneId() throws Exception {
514         builder.appendZoneId();
515         DateTimeFormatter f = builder.toFormatter();
516         assertEquals(f.toString(), "ZoneId()");
517     }
518 
519     @Test
test_appendZoneText_1arg()520     public void test_appendZoneText_1arg() throws Exception {
521         builder.appendZoneText(TextStyle.FULL);
522         DateTimeFormatter f = builder.toFormatter();
523         assertEquals(f.toString(), "ZoneText(FULL)");
524     }
525 
526     @Test(expectedExceptions=NullPointerException.class)
test_appendZoneText_1arg_nullText()527     public void test_appendZoneText_1arg_nullText() throws Exception {
528         builder.appendZoneText(null);
529     }
530 
531     //-----------------------------------------------------------------------
532     //-----------------------------------------------------------------------
533     //-----------------------------------------------------------------------
534     @Test
test_padNext_1arg()535     public void test_padNext_1arg() {
536         builder.appendValue(MONTH_OF_YEAR).appendLiteral(':').padNext(2).appendValue(DAY_OF_MONTH);
537         assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2: 1");
538     }
539 
540     @Test(expectedExceptions=IllegalArgumentException.class)
test_padNext_1arg_invalidWidth()541     public void test_padNext_1arg_invalidWidth() throws Exception {
542         builder.padNext(0);
543     }
544 
545     //-----------------------------------------------------------------------
546     @Test
test_padNext_2arg_dash()547     public void test_padNext_2arg_dash() throws Exception {
548         builder.appendValue(MONTH_OF_YEAR).appendLiteral(':').padNext(2, '-').appendValue(DAY_OF_MONTH);
549         assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2:-1");
550     }
551 
552     @Test(expectedExceptions=IllegalArgumentException.class)
test_padNext_2arg_invalidWidth()553     public void test_padNext_2arg_invalidWidth() throws Exception {
554         builder.padNext(0, '-');
555     }
556 
557     //-----------------------------------------------------------------------
558     @Test
test_padOptional()559     public void test_padOptional() throws Exception {
560         builder.appendValue(MONTH_OF_YEAR).appendLiteral(':')
561                 .padNext(5).optionalStart().appendValue(DAY_OF_MONTH).optionalEnd()
562                 .appendLiteral(':').appendValue(YEAR);
563         assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2:    1:2013");
564         assertEquals(builder.toFormatter().format(YearMonth.of(2013, 2)), "2:     :2013");
565     }
566 
567     //-----------------------------------------------------------------------
568     //-----------------------------------------------------------------------
569     //-----------------------------------------------------------------------
570     @Test
test_optionalStart_noEnd()571     public void test_optionalStart_noEnd() throws Exception {
572         builder.appendValue(MONTH_OF_YEAR).optionalStart().appendValue(DAY_OF_MONTH).appendValue(DAY_OF_WEEK);
573         DateTimeFormatter f = builder.toFormatter();
574         assertEquals(f.toString(), "Value(MonthOfYear)[Value(DayOfMonth)Value(DayOfWeek)]");
575     }
576 
577     @Test
test_optionalStart2_noEnd()578     public void test_optionalStart2_noEnd() throws Exception {
579         builder.appendValue(MONTH_OF_YEAR).optionalStart().appendValue(DAY_OF_MONTH).optionalStart().appendValue(DAY_OF_WEEK);
580         DateTimeFormatter f = builder.toFormatter();
581         assertEquals(f.toString(), "Value(MonthOfYear)[Value(DayOfMonth)[Value(DayOfWeek)]]");
582     }
583 
584     @Test
test_optionalStart_doubleStart()585     public void test_optionalStart_doubleStart() throws Exception {
586         builder.appendValue(MONTH_OF_YEAR).optionalStart().optionalStart().appendValue(DAY_OF_MONTH);
587         DateTimeFormatter f = builder.toFormatter();
588         assertEquals(f.toString(), "Value(MonthOfYear)[[Value(DayOfMonth)]]");
589     }
590 
591     //-----------------------------------------------------------------------
592     @Test
test_optionalEnd()593     public void test_optionalEnd() throws Exception {
594         builder.appendValue(MONTH_OF_YEAR).optionalStart().appendValue(DAY_OF_MONTH).optionalEnd().appendValue(DAY_OF_WEEK);
595         DateTimeFormatter f = builder.toFormatter();
596         assertEquals(f.toString(), "Value(MonthOfYear)[Value(DayOfMonth)]Value(DayOfWeek)");
597     }
598 
599     @Test
test_optionalEnd2()600     public void test_optionalEnd2() throws Exception {
601         builder.appendValue(MONTH_OF_YEAR).optionalStart().appendValue(DAY_OF_MONTH)
602             .optionalStart().appendValue(DAY_OF_WEEK).optionalEnd().appendValue(DAY_OF_MONTH).optionalEnd();
603         DateTimeFormatter f = builder.toFormatter();
604         assertEquals(f.toString(), "Value(MonthOfYear)[Value(DayOfMonth)[Value(DayOfWeek)]Value(DayOfMonth)]");
605     }
606 
607     @Test
test_optionalEnd_doubleStartSingleEnd()608     public void test_optionalEnd_doubleStartSingleEnd() throws Exception {
609         builder.appendValue(MONTH_OF_YEAR).optionalStart().optionalStart().appendValue(DAY_OF_MONTH).optionalEnd();
610         DateTimeFormatter f = builder.toFormatter();
611         assertEquals(f.toString(), "Value(MonthOfYear)[[Value(DayOfMonth)]]");
612     }
613 
614     @Test
test_optionalEnd_doubleStartDoubleEnd()615     public void test_optionalEnd_doubleStartDoubleEnd() throws Exception {
616         builder.appendValue(MONTH_OF_YEAR).optionalStart().optionalStart().appendValue(DAY_OF_MONTH).optionalEnd().optionalEnd();
617         DateTimeFormatter f = builder.toFormatter();
618         assertEquals(f.toString(), "Value(MonthOfYear)[[Value(DayOfMonth)]]");
619     }
620 
621     @Test
test_optionalStartEnd_immediateStartEnd()622     public void test_optionalStartEnd_immediateStartEnd() throws Exception {
623         builder.appendValue(MONTH_OF_YEAR).optionalStart().optionalEnd().appendValue(DAY_OF_MONTH);
624         DateTimeFormatter f = builder.toFormatter();
625         assertEquals(f.toString(), "Value(MonthOfYear)Value(DayOfMonth)");
626     }
627 
628     @Test(expectedExceptions=IllegalStateException.class)
test_optionalEnd_noStart()629     public void test_optionalEnd_noStart() throws Exception {
630         builder.optionalEnd();
631     }
632 
633     //-----------------------------------------------------------------------
634     //-----------------------------------------------------------------------
635     //-----------------------------------------------------------------------
636     @DataProvider(name="validPatterns")
dataValid()637     Object[][] dataValid() {
638         return new Object[][] {
639             {"'a'", "'a'"},
640             {"''", "''"},
641             {"'!'", "'!'"},
642             {"!", "'!'"},
643 
644             {"'hello_people,][)('", "'hello_people,][)('"},
645             {"'hi'", "'hi'"},
646             {"'yyyy'", "'yyyy'"},
647             {"''''", "''"},
648             {"'o''clock'", "'o''clock'"},
649 
650             {"G", "Text(Era,SHORT)"},
651             {"GG", "Text(Era,SHORT)"},
652             {"GGG", "Text(Era,SHORT)"},
653             {"GGGG", "Text(Era)"},
654             {"GGGGG", "Text(Era,NARROW)"},
655 
656             {"u", "Value(Year)"},
657             {"uu", "ReducedValue(Year,2,2,2000-01-01)"},
658             {"uuu", "Value(Year,3,19,NORMAL)"},
659             {"uuuu", "Value(Year,4,19,EXCEEDS_PAD)"},
660             {"uuuuu", "Value(Year,5,19,EXCEEDS_PAD)"},
661 
662             {"y", "Value(YearOfEra)"},
663             {"yy", "ReducedValue(YearOfEra,2,2,2000-01-01)"},
664             {"yyy", "Value(YearOfEra,3,19,NORMAL)"},
665             {"yyyy", "Value(YearOfEra,4,19,EXCEEDS_PAD)"},
666             {"yyyyy", "Value(YearOfEra,5,19,EXCEEDS_PAD)"},
667 
668             {"Y", "Localized(WeekBasedYear)"},
669             {"YY", "Localized(ReducedValue(WeekBasedYear,2,2,2000-01-01))"},
670             {"YYY", "Localized(WeekBasedYear,3,19,NORMAL)"},
671             {"YYYY", "Localized(WeekBasedYear,4,19,EXCEEDS_PAD)"},
672             {"YYYYY", "Localized(WeekBasedYear,5,19,EXCEEDS_PAD)"},
673 
674             {"M", "Value(MonthOfYear)"},
675             {"MM", "Value(MonthOfYear,2)"},
676             {"MMM", "Text(MonthOfYear,SHORT)"},
677             {"MMMM", "Text(MonthOfYear)"},
678             {"MMMMM", "Text(MonthOfYear,NARROW)"},
679 
680             {"L", "Value(MonthOfYear)"},
681             {"LL", "Value(MonthOfYear,2)"},
682             {"LLL", "Text(MonthOfYear,SHORT_STANDALONE)"},
683             {"LLLL", "Text(MonthOfYear,FULL_STANDALONE)"},
684             {"LLLLL", "Text(MonthOfYear,NARROW_STANDALONE)"},
685 
686             {"D", "Value(DayOfYear)"},
687             {"DD", "Value(DayOfYear,2)"},
688             {"DDD", "Value(DayOfYear,3)"},
689 
690             {"d", "Value(DayOfMonth)"},
691             {"dd", "Value(DayOfMonth,2)"},
692 
693             {"F", "Value(AlignedDayOfWeekInMonth)"},
694 
695             {"Q", "Value(QuarterOfYear)"},
696             {"QQ", "Value(QuarterOfYear,2)"},
697             {"QQQ", "Text(QuarterOfYear,SHORT)"},
698             {"QQQQ", "Text(QuarterOfYear)"},
699             {"QQQQQ", "Text(QuarterOfYear,NARROW)"},
700 
701             {"q", "Value(QuarterOfYear)"},
702             {"qq", "Value(QuarterOfYear,2)"},
703             {"qqq", "Text(QuarterOfYear,SHORT_STANDALONE)"},
704             {"qqqq", "Text(QuarterOfYear,FULL_STANDALONE)"},
705             {"qqqqq", "Text(QuarterOfYear,NARROW_STANDALONE)"},
706 
707             {"E", "Text(DayOfWeek,SHORT)"},
708             {"EE", "Text(DayOfWeek,SHORT)"},
709             {"EEE", "Text(DayOfWeek,SHORT)"},
710             {"EEEE", "Text(DayOfWeek)"},
711             {"EEEEE", "Text(DayOfWeek,NARROW)"},
712 
713             {"e", "Localized(DayOfWeek,1)"},
714             {"ee", "Localized(DayOfWeek,2)"},
715             {"eee", "Text(DayOfWeek,SHORT)"},
716             {"eeee", "Text(DayOfWeek)"},
717             {"eeeee", "Text(DayOfWeek,NARROW)"},
718 
719             {"c", "Localized(DayOfWeek,1)"},
720             {"ccc", "Text(DayOfWeek,SHORT_STANDALONE)"},
721             {"cccc", "Text(DayOfWeek,FULL_STANDALONE)"},
722             {"ccccc", "Text(DayOfWeek,NARROW_STANDALONE)"},
723 
724             {"a", "Text(AmPmOfDay,SHORT)"},
725 
726             {"H", "Value(HourOfDay)"},
727             {"HH", "Value(HourOfDay,2)"},
728 
729             {"K", "Value(HourOfAmPm)"},
730             {"KK", "Value(HourOfAmPm,2)"},
731 
732             {"k", "Value(ClockHourOfDay)"},
733             {"kk", "Value(ClockHourOfDay,2)"},
734 
735             {"h", "Value(ClockHourOfAmPm)"},
736             {"hh", "Value(ClockHourOfAmPm,2)"},
737 
738             {"m", "Value(MinuteOfHour)"},
739             {"mm", "Value(MinuteOfHour,2)"},
740 
741             {"s", "Value(SecondOfMinute)"},
742             {"ss", "Value(SecondOfMinute,2)"},
743 
744             {"S", "Fraction(NanoOfSecond,1,1)"},
745             {"SS", "Fraction(NanoOfSecond,2,2)"},
746             {"SSS", "Fraction(NanoOfSecond,3,3)"},
747             {"SSSSSSSSS", "Fraction(NanoOfSecond,9,9)"},
748 
749             {"A", "Value(MilliOfDay)"},
750             {"AA", "Value(MilliOfDay,2)"},
751             {"AAA", "Value(MilliOfDay,3)"},
752 
753             {"n", "Value(NanoOfSecond)"},
754             {"nn", "Value(NanoOfSecond,2)"},
755             {"nnn", "Value(NanoOfSecond,3)"},
756 
757             {"N", "Value(NanoOfDay)"},
758             {"NN", "Value(NanoOfDay,2)"},
759             {"NNN", "Value(NanoOfDay,3)"},
760 
761             {"z", "ZoneText(SHORT)"},
762             {"zz", "ZoneText(SHORT)"},
763             {"zzz", "ZoneText(SHORT)"},
764             {"zzzz", "ZoneText(FULL)"},
765 
766             {"VV", "ZoneId()"},
767 
768             {"Z", "Offset(+HHMM,'+0000')"},  // SimpleDateFormat
769             {"ZZ", "Offset(+HHMM,'+0000')"},  // SimpleDateFormat
770             {"ZZZ", "Offset(+HHMM,'+0000')"},  // SimpleDateFormat
771 
772             {"X", "Offset(+HHmm,'Z')"},  // LDML/almost SimpleDateFormat
773             {"XX", "Offset(+HHMM,'Z')"},  // LDML/SimpleDateFormat
774             {"XXX", "Offset(+HH:MM,'Z')"},  // LDML/SimpleDateFormat
775             {"XXXX", "Offset(+HHMMss,'Z')"},  // LDML
776             {"XXXXX", "Offset(+HH:MM:ss,'Z')"},  // LDML
777 
778             {"x", "Offset(+HHmm,'+00')"},  // LDML
779             {"xx", "Offset(+HHMM,'+0000')"},  // LDML
780             {"xxx", "Offset(+HH:MM,'+00:00')"},  // LDML
781             {"xxxx", "Offset(+HHMMss,'+0000')"},  // LDML
782             {"xxxxx", "Offset(+HH:MM:ss,'+00:00')"},  // LDML
783 
784             {"ppH", "Pad(Value(HourOfDay),2)"},
785             {"pppDD", "Pad(Value(DayOfYear,2),3)"},
786 
787             {"yyyy[-MM[-dd", "Value(YearOfEra,4,19,EXCEEDS_PAD)['-'Value(MonthOfYear,2)['-'Value(DayOfMonth,2)]]"},
788             {"yyyy[-MM[-dd]]", "Value(YearOfEra,4,19,EXCEEDS_PAD)['-'Value(MonthOfYear,2)['-'Value(DayOfMonth,2)]]"},
789             {"yyyy[-MM[]-dd]", "Value(YearOfEra,4,19,EXCEEDS_PAD)['-'Value(MonthOfYear,2)'-'Value(DayOfMonth,2)]"},
790 
791             {"yyyy-MM-dd'T'HH:mm:ss.SSS", "Value(YearOfEra,4,19,EXCEEDS_PAD)'-'Value(MonthOfYear,2)'-'Value(DayOfMonth,2)" +
792                 "'T'Value(HourOfDay,2)':'Value(MinuteOfHour,2)':'Value(SecondOfMinute,2)'.'Fraction(NanoOfSecond,3,3)"},
793 
794             {"w", "Localized(WeekOfWeekBasedYear,1)"},
795             {"ww", "Localized(WeekOfWeekBasedYear,2)"},
796             {"W", "Localized(WeekOfMonth,1)"},
797         };
798     }
799 
800     @Test(dataProvider="validPatterns")
test_appendPattern_valid(String input, String expected)801     public void test_appendPattern_valid(String input, String expected) throws Exception {
802         builder.appendPattern(input);
803         DateTimeFormatter f = builder.toFormatter();
804         assertEquals(f.toString(), expected);
805     }
806 
807     //-----------------------------------------------------------------------
808     @DataProvider(name="invalidPatterns")
dataInvalid()809     Object[][] dataInvalid() {
810         return new Object[][] {
811             {"'"},
812             {"'hello"},
813             {"'hel''lo"},
814             {"'hello''"},
815             {"{"},
816             {"}"},
817             {"{}"},
818             {"]"},
819             {"yyyy]"},
820             {"yyyy]MM"},
821             {"yyyy[MM]]"},
822 
823             {"aa"},
824             {"aaa"},
825             {"aaaa"},
826             {"aaaaa"},
827             {"aaaaaa"},
828             {"MMMMMM"},
829             {"LLLLLL"},
830             {"QQQQQQ"},
831             {"qqqqqq"},
832             {"EEEEEE"},
833             {"eeeeee"},
834             {"cc"},
835             {"cccccc"},
836             {"ddd"},
837             {"DDDD"},
838             {"FF"},
839             {"FFF"},
840             {"hhh"},
841             {"HHH"},
842             {"kkk"},
843             {"KKK"},
844             {"mmm"},
845             {"sss"},
846             {"OO"},
847             {"OOO"},
848             {"OOOOO"},
849             {"XXXXXX"},
850             {"ZZZZZZ"},
851             {"zzzzz"},
852             {"V"},
853             {"VVV"},
854             {"VVVV"},
855             {"VVVVV"},
856 
857             {"RO"},
858 
859             {"p"},
860             {"pp"},
861             {"p:"},
862 
863             {"f"},
864             {"ff"},
865             {"f:"},
866             {"fy"},
867             {"fa"},
868             {"fM"},
869 
870             {"www"},
871             {"WW"},
872         };
873     }
874 
875     @Test(dataProvider="invalidPatterns", expectedExceptions=IllegalArgumentException.class)
test_appendPattern_invalid(String input)876     public void test_appendPattern_invalid(String input) throws Exception {
877         try {
878             builder.appendPattern(input);
879         } catch (IllegalArgumentException ex) {
880             throw ex;
881         }
882     }
883 
884     //-----------------------------------------------------------------------
885     @DataProvider(name="patternPrint")
data_patternPrint()886     Object[][] data_patternPrint() {
887         return new Object[][] {
888             {"Q", date(2012, 2, 10), "1"},
889             {"QQ", date(2012, 2, 10), "01"},
890             {"QQQ", date(2012, 2, 10), "Q1"},
891             {"QQQQ", date(2012, 2, 10), "1st quarter"},
892             {"QQQQQ", date(2012, 2, 10), "1"},
893         };
894     }
895 
896     @Test(dataProvider="patternPrint")
test_appendPattern_patternPrint(String input, Temporal temporal, String expected)897     public void test_appendPattern_patternPrint(String input, Temporal temporal, String expected) throws Exception {
898         DateTimeFormatter f = builder.appendPattern(input).toFormatter(Locale.UK);
899         String test = f.format(temporal);
900         assertEquals(test, expected);
901     }
902 
903     //-----------------------------------------------------------------------
904     @DataProvider(name="localePatterns")
localizedDateTimePatterns()905     Object[][] localizedDateTimePatterns() {
906         // Android-changed: Adapt for changes since old CLDR version this tests were written for.
907         return new Object[][] {
908             {FormatStyle.FULL, FormatStyle.FULL, IsoChronology.INSTANCE, Locale.US, "EEEE, MMMM d, y 'at' h:mm:ss a zzzz"},
909             {FormatStyle.LONG, FormatStyle.LONG, IsoChronology.INSTANCE, Locale.US, "MMMM d, y 'at' h:mm:ss a z"},
910             {FormatStyle.MEDIUM, FormatStyle.MEDIUM, IsoChronology.INSTANCE, Locale.US, "MMM d, y, h:mm:ss a"},
911             {FormatStyle.SHORT, FormatStyle.SHORT, IsoChronology.INSTANCE, Locale.US, "M/d/yy, h:mm a"},
912             {FormatStyle.FULL, null, IsoChronology.INSTANCE, Locale.US, "EEEE, MMMM d, y"},
913             {FormatStyle.LONG, null, IsoChronology.INSTANCE, Locale.US, "MMMM d, y"},
914             {FormatStyle.MEDIUM, null, IsoChronology.INSTANCE, Locale.US, "MMM d, y"},
915             {FormatStyle.SHORT, null, IsoChronology.INSTANCE, Locale.US, "M/d/yy"},
916             {null, FormatStyle.FULL, IsoChronology.INSTANCE, Locale.US, "h:mm:ss a zzzz"},
917             {null, FormatStyle.LONG, IsoChronology.INSTANCE, Locale.US, "h:mm:ss a z"},
918             {null, FormatStyle.MEDIUM, IsoChronology.INSTANCE, Locale.US, "h:mm:ss a"},
919             {null, FormatStyle.SHORT, IsoChronology.INSTANCE, Locale.US, "h:mm a"},
920 
921             // French Locale and ISO Chronology
922             {FormatStyle.FULL, FormatStyle.FULL, IsoChronology.INSTANCE, Locale.FRENCH, "EEEE d MMMM y 'à' HH:mm:ss zzzz"},
923             {FormatStyle.LONG, FormatStyle.LONG, IsoChronology.INSTANCE, Locale.FRENCH, "d MMMM y 'à' HH:mm:ss z"},
924             {FormatStyle.MEDIUM, FormatStyle.MEDIUM, IsoChronology.INSTANCE, Locale.FRENCH, "d MMM y 'à' HH:mm:ss"},
925             {FormatStyle.SHORT, FormatStyle.SHORT, IsoChronology.INSTANCE, Locale.FRENCH, "dd/MM/y HH:mm"},
926             {FormatStyle.FULL, null, IsoChronology.INSTANCE, Locale.FRENCH, "EEEE d MMMM y"},
927             {FormatStyle.LONG, null, IsoChronology.INSTANCE, Locale.FRENCH, "d MMMM y"},
928             {FormatStyle.MEDIUM, null, IsoChronology.INSTANCE, Locale.FRENCH, "d MMM y"},
929             {FormatStyle.SHORT, null, IsoChronology.INSTANCE, Locale.FRENCH, "dd/MM/y"},
930             {null, FormatStyle.FULL, IsoChronology.INSTANCE, Locale.FRENCH, "HH:mm:ss zzzz"},
931             {null, FormatStyle.LONG, IsoChronology.INSTANCE, Locale.FRENCH, "HH:mm:ss z"},
932             {null, FormatStyle.MEDIUM, IsoChronology.INSTANCE, Locale.FRENCH, "HH:mm:ss"},
933             {null, FormatStyle.SHORT, IsoChronology.INSTANCE, Locale.FRENCH, "HH:mm"},
934 
935             // Japanese Locale and JapaneseChronology
936             {FormatStyle.FULL, FormatStyle.FULL, JapaneseChronology.INSTANCE, Locale.JAPANESE, "Gy\u5e74M\u6708d\u65e5EEEE H\u6642mm\u5206ss\u79d2 zzzz"},
937             {FormatStyle.LONG, FormatStyle.LONG, JapaneseChronology.INSTANCE, Locale.JAPANESE, "Gy\u5e74M\u6708d\u65e5 H:mm:ss z"},
938             {FormatStyle.MEDIUM, FormatStyle.MEDIUM, JapaneseChronology.INSTANCE, Locale.JAPANESE, "Gy\u5e74M\u6708d\u65e5 H:mm:ss"},
939             {FormatStyle.SHORT, FormatStyle.SHORT, JapaneseChronology.INSTANCE, Locale.JAPANESE, "GGGGGy/M/d H:mm"},
940             {FormatStyle.FULL, null, JapaneseChronology.INSTANCE, Locale.JAPANESE, "Gy\u5e74M\u6708d\u65e5EEEE"},
941             {FormatStyle.LONG, null, JapaneseChronology.INSTANCE, Locale.JAPANESE, "Gy\u5e74M\u6708d\u65e5"},
942             {FormatStyle.MEDIUM, null, JapaneseChronology.INSTANCE, Locale.JAPANESE, "Gy\u5e74M\u6708d\u65e5"},
943             {FormatStyle.SHORT, null, JapaneseChronology.INSTANCE, Locale.JAPANESE, "GGGGGy/M/d"},
944             {null, FormatStyle.FULL, JapaneseChronology.INSTANCE, Locale.JAPANESE, "H\u6642mm\u5206ss\u79d2 zzzz"},
945             {null, FormatStyle.LONG, JapaneseChronology.INSTANCE, Locale.JAPANESE, "H:mm:ss z"},
946             {null, FormatStyle.MEDIUM, JapaneseChronology.INSTANCE, Locale.JAPANESE, "H:mm:ss"},
947             {null, FormatStyle.SHORT, JapaneseChronology.INSTANCE, Locale.JAPANESE, "H:mm"},
948 
949             // Chinese Local and Chronology
950             {FormatStyle.FULL, FormatStyle.FULL, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5EEEE zzzz ah:mm:ss"},
951             {FormatStyle.LONG, FormatStyle.LONG, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5 z ah:mm:ss"},
952             {FormatStyle.MEDIUM, FormatStyle.MEDIUM, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5 ah:mm:ss"},
953             {FormatStyle.SHORT, FormatStyle.SHORT, MinguoChronology.INSTANCE, Locale.CHINESE, "Gyy/M/d ah:mm"},
954             {FormatStyle.FULL, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5EEEE"},
955             {FormatStyle.LONG, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5"},
956             {FormatStyle.MEDIUM, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5"},
957             {FormatStyle.SHORT, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gyy/M/d"},
958             {null, FormatStyle.FULL, MinguoChronology.INSTANCE, Locale.CHINESE, "zzzz ah:mm:ss"},
959             {null, FormatStyle.LONG, MinguoChronology.INSTANCE, Locale.CHINESE, "z ah:mm:ss"},
960             {null, FormatStyle.MEDIUM, MinguoChronology.INSTANCE, Locale.CHINESE, "ah:mm:ss"},
961             {null, FormatStyle.SHORT, MinguoChronology.INSTANCE, Locale.CHINESE, "ah:mm"},
962         };
963     }
964 
965     @Test(dataProvider="localePatterns")
test_getLocalizedDateTimePattern(FormatStyle dateStyle, FormatStyle timeStyle, Chronology chrono, Locale locale, String expected)966     public void test_getLocalizedDateTimePattern(FormatStyle dateStyle, FormatStyle timeStyle,
967             Chronology chrono, Locale locale, String expected) {
968         String actual = DateTimeFormatterBuilder.getLocalizedDateTimePattern(dateStyle, timeStyle, chrono, locale);
969         assertEquals(actual, expected, "Pattern " + convertNonAscii(actual));
970     }
971 
972     @Test(expectedExceptions=java.lang.IllegalArgumentException.class)
test_getLocalizedDateTimePatternIAE()973     public void test_getLocalizedDateTimePatternIAE() {
974         DateTimeFormatterBuilder.getLocalizedDateTimePattern(null, null, IsoChronology.INSTANCE, Locale.US);
975     }
976 
977     @Test(expectedExceptions=java.lang.NullPointerException.class)
test_getLocalizedChronoNPE()978     public void test_getLocalizedChronoNPE() {
979         DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.SHORT, FormatStyle.SHORT, null, Locale.US);
980     }
981 
982     @Test(expectedExceptions=java.lang.NullPointerException.class)
test_getLocalizedLocaleNPE()983     public void test_getLocalizedLocaleNPE() {
984         DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.SHORT, FormatStyle.SHORT, IsoChronology.INSTANCE, null);
985     }
986 
987     /**
988      * Returns a string that includes non-ascii characters after expanding
989      * the non-ascii characters to their Java language \\uxxxx form.
990      * @param input an input string
991      * @return the encoded string.
992      */
convertNonAscii(String input)993     private String convertNonAscii(String input) {
994         StringBuilder sb = new StringBuilder(input.length() * 6);
995         for (int i = 0; i < input.length(); i++) {
996             char ch = input.charAt(i);
997             if (ch < 255) {
998                 sb.append(ch);
999             } else {
1000                 sb.append("\\u");
1001                 sb.append(Integer.toHexString(ch));
1002             }
1003         }
1004         return sb.toString();
1005     }
1006 
date(int y, int m, int d)1007     private static Temporal date(int y, int m, int d) {
1008         return LocalDate.of(y, m, d);
1009     }
1010 
1011 }
1012