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 tck.java.time.format;
61 
62 import static java.time.temporal.ChronoField.DAY_OF_MONTH;
63 import static java.time.temporal.ChronoField.HOUR_OF_DAY;
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.NANO_OF_SECOND;
67 import static java.time.temporal.ChronoField.YEAR;
68 import static org.testng.Assert.assertEquals;
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.format.DateTimeFormatter;
75 import java.time.format.DateTimeFormatterBuilder;
76 import java.time.format.SignStyle;
77 import java.time.format.TextStyle;
78 import java.time.temporal.Temporal;
79 import java.time.temporal.TemporalAccessor;
80 import java.util.HashMap;
81 import java.util.Locale;
82 import java.util.Map;
83 
84 import org.testng.annotations.BeforeMethod;
85 import org.testng.annotations.DataProvider;
86 import org.testng.annotations.Test;
87 
88 /**
89  * Test DateTimeFormatterBuilder.
90  */
91 @Test
92 public class TCKDateTimeFormatterBuilder {
93 
94     private DateTimeFormatterBuilder builder;
95 
96     @BeforeMethod
setUp()97     public void setUp() {
98         builder = new DateTimeFormatterBuilder();
99     }
100 
101     //-----------------------------------------------------------------------
102     @Test
test_toFormatter_empty()103     public void test_toFormatter_empty() throws Exception {
104         DateTimeFormatter f = builder.toFormatter();
105         assertEquals(f.format(LocalDate.of(2012, 6, 30)), "");
106     }
107 
108     //-----------------------------------------------------------------------
109     @Test
test_parseDefaulting_entireDate()110     public void test_parseDefaulting_entireDate() {
111         DateTimeFormatter f = builder
112             .parseDefaulting(YEAR, 2012).parseDefaulting(MONTH_OF_YEAR, 6)
113             .parseDefaulting(DAY_OF_MONTH, 30).toFormatter();
114         LocalDate parsed = f.parse("", LocalDate::from);  // blank string can be parsed
115         assertEquals(parsed, LocalDate.of(2012, 6, 30));
116     }
117 
118     @Test
test_parseDefaulting_yearOptionalMonthOptionalDay()119     public void test_parseDefaulting_yearOptionalMonthOptionalDay() {
120         DateTimeFormatter f = builder
121                 .appendValue(YEAR)
122                 .optionalStart().appendLiteral('-').appendValue(MONTH_OF_YEAR)
123                 .optionalStart().appendLiteral('-').appendValue(DAY_OF_MONTH)
124                 .optionalEnd().optionalEnd()
125                 .parseDefaulting(MONTH_OF_YEAR, 1)
126                 .parseDefaulting(DAY_OF_MONTH, 1).toFormatter();
127         assertEquals(f.parse("2012", LocalDate::from), LocalDate.of(2012, 1, 1));
128         assertEquals(f.parse("2012-6", LocalDate::from), LocalDate.of(2012, 6, 1));
129         assertEquals(f.parse("2012-6-30", LocalDate::from), LocalDate.of(2012, 6, 30));
130     }
131 
132     @Test(expectedExceptions = NullPointerException.class)
test_parseDefaulting_null()133     public void test_parseDefaulting_null() {
134         builder.parseDefaulting(null, 1);
135     }
136 
137     //-----------------------------------------------------------------------
138     @Test(expectedExceptions=NullPointerException.class)
test_appendValue_1arg_null()139     public void test_appendValue_1arg_null() throws Exception {
140         builder.appendValue(null);
141     }
142 
143     //-----------------------------------------------------------------------
144     @Test(expectedExceptions=NullPointerException.class)
test_appendValue_2arg_null()145     public void test_appendValue_2arg_null() throws Exception {
146         builder.appendValue(null, 3);
147     }
148 
149     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_2arg_widthTooSmall()150     public void test_appendValue_2arg_widthTooSmall() throws Exception {
151         builder.appendValue(DAY_OF_MONTH, 0);
152     }
153 
154     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_2arg_widthTooBig()155     public void test_appendValue_2arg_widthTooBig() throws Exception {
156         builder.appendValue(DAY_OF_MONTH, 20);
157     }
158 
159     //-----------------------------------------------------------------------
160     @Test(expectedExceptions=NullPointerException.class)
test_appendValue_3arg_nullField()161     public void test_appendValue_3arg_nullField() throws Exception {
162         builder.appendValue(null, 2, 3, SignStyle.NORMAL);
163     }
164 
165     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_3arg_minWidthTooSmall()166     public void test_appendValue_3arg_minWidthTooSmall() throws Exception {
167         builder.appendValue(DAY_OF_MONTH, 0, 2, SignStyle.NORMAL);
168     }
169 
170     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_3arg_minWidthTooBig()171     public void test_appendValue_3arg_minWidthTooBig() throws Exception {
172         builder.appendValue(DAY_OF_MONTH, 20, 2, SignStyle.NORMAL);
173     }
174 
175     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_3arg_maxWidthTooSmall()176     public void test_appendValue_3arg_maxWidthTooSmall() throws Exception {
177         builder.appendValue(DAY_OF_MONTH, 2, 0, SignStyle.NORMAL);
178     }
179 
180     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_3arg_maxWidthTooBig()181     public void test_appendValue_3arg_maxWidthTooBig() throws Exception {
182         builder.appendValue(DAY_OF_MONTH, 2, 20, SignStyle.NORMAL);
183     }
184 
185     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_3arg_maxWidthMinWidth()186     public void test_appendValue_3arg_maxWidthMinWidth() throws Exception {
187         builder.appendValue(DAY_OF_MONTH, 4, 2, SignStyle.NORMAL);
188     }
189 
190     @Test(expectedExceptions=NullPointerException.class)
test_appendValue_3arg_nullSignStyle()191     public void test_appendValue_3arg_nullSignStyle() throws Exception {
192         builder.appendValue(DAY_OF_MONTH, 2, 3, null);
193     }
194 
195     //-----------------------------------------------------------------------
196     @Test(expectedExceptions=NullPointerException.class)
test_appendValueReduced_int_nullField()197     public void test_appendValueReduced_int_nullField() throws Exception {
198         builder.appendValueReduced(null, 2, 2, 2000);
199     }
200 
201     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValueReduced_int_minWidthTooSmall()202     public void test_appendValueReduced_int_minWidthTooSmall() throws Exception {
203         builder.appendValueReduced(YEAR, 0, 2, 2000);
204     }
205 
206     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValueReduced_int_minWidthTooBig()207     public void test_appendValueReduced_int_minWidthTooBig() throws Exception {
208         builder.appendValueReduced(YEAR, 11, 2, 2000);
209     }
210 
211     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValueReduced_int_maxWidthTooSmall()212     public void test_appendValueReduced_int_maxWidthTooSmall() throws Exception {
213         builder.appendValueReduced(YEAR, 2, 0, 2000);
214     }
215 
216     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValueReduced_int_maxWidthTooBig()217     public void test_appendValueReduced_int_maxWidthTooBig() throws Exception {
218         builder.appendValueReduced(YEAR, 2, 11, 2000);
219     }
220 
221     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValueReduced_int_maxWidthLessThanMin()222     public void test_appendValueReduced_int_maxWidthLessThanMin() throws Exception {
223         builder.appendValueReduced(YEAR, 2, 1, 2000);
224     }
225 
226     //-----------------------------------------------------------------------
227     @Test(expectedExceptions=NullPointerException.class)
test_appendValueReduced_date_nullField()228     public void test_appendValueReduced_date_nullField() throws Exception {
229         builder.appendValueReduced(null, 2, 2, LocalDate.of(2000, 1, 1));
230     }
231 
232     @Test(expectedExceptions=NullPointerException.class)
test_appendValueReduced_date_nullDate()233     public void test_appendValueReduced_date_nullDate() throws Exception {
234         builder.appendValueReduced(YEAR, 2, 2, null);
235     }
236 
237     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValueReduced_date_minWidthTooSmall()238     public void test_appendValueReduced_date_minWidthTooSmall() throws Exception {
239         builder.appendValueReduced(YEAR, 0, 2, LocalDate.of(2000, 1, 1));
240     }
241 
242     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValueReduced_date_minWidthTooBig()243     public void test_appendValueReduced_date_minWidthTooBig() throws Exception {
244         builder.appendValueReduced(YEAR, 11, 2, LocalDate.of(2000, 1, 1));
245     }
246 
247     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValueReduced_date_maxWidthTooSmall()248     public void test_appendValueReduced_date_maxWidthTooSmall() throws Exception {
249         builder.appendValueReduced(YEAR, 2, 0, LocalDate.of(2000, 1, 1));
250     }
251 
252     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValueReduced_date_maxWidthTooBig()253     public void test_appendValueReduced_date_maxWidthTooBig() throws Exception {
254         builder.appendValueReduced(YEAR, 2, 11, LocalDate.of(2000, 1, 1));
255     }
256 
257     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValueReduced_date_maxWidthLessThanMin()258     public void test_appendValueReduced_date_maxWidthLessThanMin() throws Exception {
259         builder.appendValueReduced(YEAR, 2, 1, LocalDate.of(2000, 1, 1));
260     }
261 
262     //-----------------------------------------------------------------------
263     //-----------------------------------------------------------------------
264     //-----------------------------------------------------------------------
265     @Test(expectedExceptions=NullPointerException.class)
test_appendFraction_4arg_nullRule()266     public void test_appendFraction_4arg_nullRule() throws Exception {
267         builder.appendFraction(null, 1, 9, false);
268     }
269 
270     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendFraction_4arg_invalidRuleNotFixedSet()271     public void test_appendFraction_4arg_invalidRuleNotFixedSet() throws Exception {
272         builder.appendFraction(DAY_OF_MONTH, 1, 9, false);
273     }
274 
275     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendFraction_4arg_minTooSmall()276     public void test_appendFraction_4arg_minTooSmall() throws Exception {
277         builder.appendFraction(MINUTE_OF_HOUR, -1, 9, false);
278     }
279 
280     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendFraction_4arg_minTooBig()281     public void test_appendFraction_4arg_minTooBig() throws Exception {
282         builder.appendFraction(MINUTE_OF_HOUR, 10, 9, false);
283     }
284 
285     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendFraction_4arg_maxTooSmall()286     public void test_appendFraction_4arg_maxTooSmall() throws Exception {
287         builder.appendFraction(MINUTE_OF_HOUR, 0, -1, false);
288     }
289 
290     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendFraction_4arg_maxTooBig()291     public void test_appendFraction_4arg_maxTooBig() throws Exception {
292         builder.appendFraction(MINUTE_OF_HOUR, 1, 10, false);
293     }
294 
295     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendFraction_4arg_maxWidthMinWidth()296     public void test_appendFraction_4arg_maxWidthMinWidth() throws Exception {
297         builder.appendFraction(MINUTE_OF_HOUR, 9, 3, false);
298     }
299 
300     //-----------------------------------------------------------------------
301     //-----------------------------------------------------------------------
302     //-----------------------------------------------------------------------
303     @Test(expectedExceptions=NullPointerException.class)
test_appendText_1arg_null()304     public void test_appendText_1arg_null() throws Exception {
305         builder.appendText(null);
306     }
307 
308     //-----------------------------------------------------------------------
309     @Test(expectedExceptions=NullPointerException.class)
test_appendText_2arg_nullRule()310     public void test_appendText_2arg_nullRule() throws Exception {
311         builder.appendText(null, TextStyle.SHORT);
312     }
313 
314     @Test(expectedExceptions=NullPointerException.class)
test_appendText_2arg_nullStyle()315     public void test_appendText_2arg_nullStyle() throws Exception {
316         builder.appendText(MONTH_OF_YEAR, (TextStyle) null);
317     }
318 
319     //-----------------------------------------------------------------------
320     @Test(expectedExceptions=NullPointerException.class)
test_appendTextMap_nullRule()321     public void test_appendTextMap_nullRule() throws Exception {
322         builder.appendText(null, new HashMap<>());
323     }
324 
325     @Test(expectedExceptions=NullPointerException.class)
test_appendTextMap_nullStyle()326     public void test_appendTextMap_nullStyle() throws Exception {
327         builder.appendText(MONTH_OF_YEAR, (Map<Long, String>) null);
328     }
329 
330     //-----------------------------------------------------------------------
331     //-----------------------------------------------------------------------
332     //-----------------------------------------------------------------------
333     @DataProvider(name="offsetPatterns")
data_offsetPatterns()334     Object[][] data_offsetPatterns() {
335         return new Object[][] {
336                 {"+HH", 2, 0, 0, "+02"},
337                 {"+HH", -2, 0, 0, "-02"},
338                 {"+HH", 2, 30, 0, "+02"},
339                 {"+HH", 2, 0, 45, "+02"},
340                 {"+HH", 2, 30, 45, "+02"},
341 
342                 {"+HHMM", 2, 0, 0, "+0200"},
343                 {"+HHMM", -2, 0, 0, "-0200"},
344                 {"+HHMM", 2, 30, 0, "+0230"},
345                 {"+HHMM", 2, 0, 45, "+0200"},
346                 {"+HHMM", 2, 30, 45, "+0230"},
347 
348                 {"+HH:MM", 2, 0, 0, "+02:00"},
349                 {"+HH:MM", -2, 0, 0, "-02:00"},
350                 {"+HH:MM", 2, 30, 0, "+02:30"},
351                 {"+HH:MM", 2, 0, 45, "+02:00"},
352                 {"+HH:MM", 2, 30, 45, "+02:30"},
353 
354                 {"+HHMMss", 2, 0, 0, "+0200"},
355                 {"+HHMMss", -2, 0, 0, "-0200"},
356                 {"+HHMMss", 2, 30, 0, "+0230"},
357                 {"+HHMMss", 2, 0, 45, "+020045"},
358                 {"+HHMMss", 2, 30, 45, "+023045"},
359 
360                 {"+HH:MM:ss", 2, 0, 0, "+02:00"},
361                 {"+HH:MM:ss", -2, 0, 0, "-02:00"},
362                 {"+HH:MM:ss", 2, 30, 0, "+02:30"},
363                 {"+HH:MM:ss", 2, 0, 45, "+02:00:45"},
364                 {"+HH:MM:ss", 2, 30, 45, "+02:30:45"},
365 
366                 {"+HHMMSS", 2, 0, 0, "+020000"},
367                 {"+HHMMSS", -2, 0, 0, "-020000"},
368                 {"+HHMMSS", 2, 30, 0, "+023000"},
369                 {"+HHMMSS", 2, 0, 45, "+020045"},
370                 {"+HHMMSS", 2, 30, 45, "+023045"},
371 
372                 {"+HH:MM:SS", 2, 0, 0, "+02:00:00"},
373                 {"+HH:MM:SS", -2, 0, 0, "-02:00:00"},
374                 {"+HH:MM:SS", 2, 30, 0, "+02:30:00"},
375                 {"+HH:MM:SS", 2, 0, 45, "+02:00:45"},
376                 {"+HH:MM:SS", 2, 30, 45, "+02:30:45"},
377         };
378     }
379 
380     @Test(dataProvider="offsetPatterns")
test_appendOffset_format(String pattern, int h, int m, int s, String expected)381     public void test_appendOffset_format(String pattern, int h, int m, int s, String expected) throws Exception {
382         builder.appendOffset(pattern, "Z");
383         DateTimeFormatter f = builder.toFormatter();
384         ZoneOffset offset = ZoneOffset.ofHoursMinutesSeconds(h, m, s);
385         assertEquals(f.format(offset), expected);
386     }
387 
388     @Test(dataProvider="offsetPatterns")
test_appendOffset_parse(String pattern, int h, int m, int s, String expected)389     public void test_appendOffset_parse(String pattern, int h, int m, int s, String expected) throws Exception {
390         builder.appendOffset(pattern, "Z");
391         DateTimeFormatter f = builder.toFormatter();
392         ZoneOffset parsed = f.parse(expected, ZoneOffset::from);
393         assertEquals(f.format(parsed), expected);
394     }
395 
396     @DataProvider(name="badOffsetPatterns")
data_badOffsetPatterns()397     Object[][] data_badOffsetPatterns() {
398         return new Object[][] {
399             {"HH"},
400             {"HHMM"},
401             {"HH:MM"},
402             {"HHMMss"},
403             {"HH:MM:ss"},
404             {"HHMMSS"},
405             {"HH:MM:SS"},
406             {"+H"},
407             {"+HMM"},
408             {"+HHM"},
409             {"+A"},
410         };
411     }
412 
413     @Test(dataProvider="badOffsetPatterns", expectedExceptions=IllegalArgumentException.class)
test_appendOffset_badPattern(String pattern)414     public void test_appendOffset_badPattern(String pattern) throws Exception {
415         builder.appendOffset(pattern, "Z");
416     }
417 
418     @Test(expectedExceptions=NullPointerException.class)
test_appendOffset_3arg_nullText()419     public void test_appendOffset_3arg_nullText() throws Exception {
420         builder.appendOffset("+HH:MM", null);
421     }
422 
423     @Test(expectedExceptions=NullPointerException.class)
test_appendOffset_3arg_nullPattern()424     public void test_appendOffset_3arg_nullPattern() throws Exception {
425         builder.appendOffset(null, "Z");
426     }
427 
428     //-----------------------------------------------------------------------
429     //-----------------------------------------------------------------------
430     //-----------------------------------------------------------------------
431     @Test(expectedExceptions=NullPointerException.class)
test_appendZoneText_1arg_nullText()432     public void test_appendZoneText_1arg_nullText() throws Exception {
433         builder.appendZoneText(null);
434     }
435 
436     //-----------------------------------------------------------------------
437     //-----------------------------------------------------------------------
438     //-----------------------------------------------------------------------
439     @Test
test_padNext_1arg()440     public void test_padNext_1arg() {
441         builder.appendValue(MONTH_OF_YEAR).appendLiteral(':').padNext(2).appendValue(DAY_OF_MONTH);
442         assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2: 1");
443     }
444 
445     @Test(expectedExceptions=IllegalArgumentException.class)
test_padNext_1arg_invalidWidth()446     public void test_padNext_1arg_invalidWidth() throws Exception {
447         builder.padNext(0);
448     }
449 
450     //-----------------------------------------------------------------------
451     @Test
test_padNext_2arg_dash()452     public void test_padNext_2arg_dash() throws Exception {
453         builder.appendValue(MONTH_OF_YEAR).appendLiteral(':').padNext(2, '-').appendValue(DAY_OF_MONTH);
454         assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2:-1");
455     }
456 
457     @Test(expectedExceptions=IllegalArgumentException.class)
test_padNext_2arg_invalidWidth()458     public void test_padNext_2arg_invalidWidth() throws Exception {
459         builder.padNext(0, '-');
460     }
461 
462     //-----------------------------------------------------------------------
463     @Test
test_padOptional()464     public void test_padOptional() throws Exception {
465         builder.appendValue(MONTH_OF_YEAR).appendLiteral(':')
466                 .padNext(5).optionalStart().appendValue(DAY_OF_MONTH).optionalEnd()
467                 .appendLiteral(':').appendValue(YEAR);
468         assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2:    1:2013");
469         assertEquals(builder.toFormatter().format(YearMonth.of(2013, 2)), "2:     :2013");
470     }
471 
472     //-----------------------------------------------------------------------
473     //-----------------------------------------------------------------------
474     //-----------------------------------------------------------------------
475     @Test(expectedExceptions=IllegalStateException.class)
test_optionalEnd_noStart()476     public void test_optionalEnd_noStart() throws Exception {
477         builder.optionalEnd();
478     }
479 
480     //-----------------------------------------------------------------------
481     //-----------------------------------------------------------------------
482     //-----------------------------------------------------------------------
483     @DataProvider(name="validPatterns")
dataValid()484     Object[][] dataValid() {
485         return new Object[][] {
486             {"'a'"},
487             {"''"},
488             {"'!'"},
489             {"!"},
490             {"'#'"},
491 
492             {"'hello_people,][)('"},
493             {"'hi'"},
494             {"'yyyy'"},
495             {"''''"},
496             {"'o''clock'"},
497 
498             {"G"},
499             {"GG"},
500             {"GGG"},
501             {"GGGG"},
502             {"GGGGG"},
503 
504             {"y"},
505             {"yy"},
506             {"yyy"},
507             {"yyyy"},
508             {"yyyyy"},
509 
510             {"M"},
511             {"MM"},
512             {"MMM"},
513             {"MMMM"},
514             {"MMMMM"},
515 
516             {"L"},
517             {"LL"},
518             {"LLL"},
519             {"LLLL"},
520             {"LLLLL"},
521 
522             {"D"},
523             {"DD"},
524             {"DDD"},
525 
526             {"d"},
527             {"dd"},
528 
529             {"F"},
530 
531             {"Q"},
532             {"QQ"},
533             {"QQQ"},
534             {"QQQQ"},
535             {"QQQQQ"},
536 
537             {"q"},
538             {"qq"},
539             {"qqq"},
540             {"qqqq"},
541             {"qqqqq"},
542 
543             {"E"},
544             {"EE"},
545             {"EEE"},
546             {"EEEE"},
547             {"EEEEE"},
548 
549             {"e"},
550             {"ee"},
551             {"eee"},
552             {"eeee"},
553             {"eeeee"},
554 
555             {"c"},
556             {"ccc"},
557             {"cccc"},
558             {"ccccc"},
559 
560             {"a"},
561 
562             {"H"},
563             {"HH"},
564 
565             {"K"},
566             {"KK"},
567 
568             {"k"},
569             {"kk"},
570 
571             {"h"},
572             {"hh"},
573 
574             {"m"},
575             {"mm"},
576 
577             {"s"},
578             {"ss"},
579 
580             {"S"},
581             {"SS"},
582             {"SSS"},
583             {"SSSSSSSSS"},
584 
585             {"A"},
586             {"AA"},
587             {"AAA"},
588 
589             {"n"},
590             {"nn"},
591             {"nnn"},
592 
593             {"N"},
594             {"NN"},
595             {"NNN"},
596 
597             {"z"},
598             {"zz"},
599             {"zzz"},
600             {"zzzz"},
601 
602             {"VV"},
603 
604             {"Z"},
605             {"ZZ"},
606             {"ZZZ"},
607 
608             {"X"},
609             {"XX"},
610             {"XXX"},
611             {"XXXX"},
612             {"XXXXX"},
613 
614             {"x"},
615             {"xx"},
616             {"xxx"},
617             {"xxxx"},
618             {"xxxxx"},
619 
620             {"ppH"},
621             {"pppDD"},
622 
623             {"yyyy[-MM[-dd"},
624             {"yyyy[-MM[-dd]]"},
625             {"yyyy[-MM[]-dd]"},
626 
627             {"yyyy-MM-dd'T'HH:mm:ss.SSS"},
628 
629             {"e"},
630             {"w"},
631             {"ww"},
632             {"W"},
633             {"W"},
634 
635         };
636     }
637 
638     @Test(dataProvider="validPatterns")
test_appendPattern_valid(String input)639     public void test_appendPattern_valid(String input) throws Exception {
640         builder.appendPattern(input);  // test is for no error here
641     }
642 
643     //-----------------------------------------------------------------------
644     @DataProvider(name="invalidPatterns")
dataInvalid()645     Object[][] dataInvalid() {
646         return new Object[][] {
647             {"'"},
648             {"'hello"},
649             {"'hel''lo"},
650             {"'hello''"},
651             {"{"},
652             {"}"},
653             {"{}"},
654             {"#"},
655             {"]"},
656             {"yyyy]"},
657             {"yyyy]MM"},
658             {"yyyy[MM]]"},
659 
660             {"aa"},
661             {"aaa"},
662             {"aaaa"},
663             {"aaaaa"},
664             {"aaaaaa"},
665             {"MMMMMM"},
666             {"QQQQQQ"},
667             {"qqqqqq"},
668             {"EEEEEE"},
669             {"eeeeee"},
670             {"cc"},
671             {"cccccc"},
672             {"ddd"},
673             {"DDDD"},
674             {"FF"},
675             {"FFF"},
676             {"hhh"},
677             {"HHH"},
678             {"kkk"},
679             {"KKK"},
680             {"mmm"},
681             {"sss"},
682             {"OO"},
683             {"OOO"},
684             {"OOOOO"},
685             {"XXXXXX"},
686             {"zzzzz"},
687             {"ZZZZZZ"},
688 
689             {"RO"},
690 
691             {"p"},
692             {"pp"},
693             {"p:"},
694 
695             {"f"},
696             {"ff"},
697             {"f:"},
698             {"fy"},
699             {"fa"},
700             {"fM"},
701 
702             {"www"},
703             {"WW"},
704         };
705     }
706 
707     @Test(dataProvider="invalidPatterns", expectedExceptions=IllegalArgumentException.class)
test_appendPattern_invalid(String input)708     public void test_appendPattern_invalid(String input) throws Exception {
709         builder.appendPattern(input);  // test is for error here
710     }
711 
712     //-----------------------------------------------------------------------
713     @DataProvider(name="patternPrint")
data_patternPrint()714     Object[][] data_patternPrint() {
715         return new Object[][] {
716             {"Q", date(2012, 2, 10), "1"},
717             {"QQ", date(2012, 2, 10), "01"},
718             {"QQQ", date(2012, 2, 10), "Q1"},
719             {"QQQQ", date(2012, 2, 10), "1st quarter"},
720             {"QQQQQ", date(2012, 2, 10), "1"},
721         };
722     }
723 
724     @Test(dataProvider="patternPrint")
test_appendPattern_patternPrint(String input, Temporal temporal, String expected)725     public void test_appendPattern_patternPrint(String input, Temporal temporal, String expected) throws Exception {
726         DateTimeFormatter f = builder.appendPattern(input).toFormatter(Locale.UK);
727         String test = f.format(temporal);
728         assertEquals(test, expected);
729     }
730 
date(int y, int m, int d)731     private static Temporal date(int y, int m, int d) {
732         return LocalDate.of(y, m, d);
733     }
734 
735     //-----------------------------------------------------------------------
736     @Test
test_adjacent_strict_firstFixedWidth()737     public void test_adjacent_strict_firstFixedWidth() throws Exception {
738         // succeeds because both number elements are fixed width
739         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK);
740         ParsePosition pp = new ParsePosition(0);
741         TemporalAccessor parsed = f.parseUnresolved("12309", pp);
742         assertEquals(pp.getErrorIndex(), -1);
743         assertEquals(pp.getIndex(), 5);
744         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
745         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
746     }
747 
748     @Test
test_adjacent_strict_firstVariableWidth_success()749     public void test_adjacent_strict_firstVariableWidth_success() throws Exception {
750         // succeeds greedily parsing variable width, then fixed width, to non-numeric Z
751         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('Z').toFormatter(Locale.UK);
752         ParsePosition pp = new ParsePosition(0);
753         TemporalAccessor parsed = f.parseUnresolved("12309Z", pp);
754         assertEquals(pp.getErrorIndex(), -1);
755         assertEquals(pp.getIndex(), 6);
756         assertEquals(parsed.getLong(HOUR_OF_DAY), 123L);
757         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 9L);
758     }
759 
760     @Test
test_adjacent_strict_firstVariableWidth_fails()761     public void test_adjacent_strict_firstVariableWidth_fails() throws Exception {
762         // fails because literal is a number and variable width parse greedily absorbs it
763         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK);
764         ParsePosition pp = new ParsePosition(0);
765         TemporalAccessor parsed = f.parseUnresolved("12309", pp);
766         assertEquals(pp.getErrorIndex(), 5);
767         assertEquals(parsed, null);
768     }
769 
770     @Test
test_adjacent_lenient()771     public void test_adjacent_lenient() throws Exception {
772         // succeeds because both number elements are fixed width even in lenient mode
773         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK);
774         ParsePosition pp = new ParsePosition(0);
775         TemporalAccessor parsed = f.parseUnresolved("12309", pp);
776         assertEquals(pp.getErrorIndex(), -1);
777         assertEquals(pp.getIndex(), 5);
778         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
779         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
780     }
781 
782     @Test
test_adjacent_lenient_firstVariableWidth_success()783     public void test_adjacent_lenient_firstVariableWidth_success() throws Exception {
784         // succeeds greedily parsing variable width, then fixed width, to non-numeric Z
785         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('Z').toFormatter(Locale.UK);
786         ParsePosition pp = new ParsePosition(0);
787         TemporalAccessor parsed = f.parseUnresolved("12309Z", pp);
788         assertEquals(pp.getErrorIndex(), -1);
789         assertEquals(pp.getIndex(), 6);
790         assertEquals(parsed.getLong(HOUR_OF_DAY), 123L);
791         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 9L);
792     }
793 
794     @Test
test_adjacent_lenient_firstVariableWidth_fails()795     public void test_adjacent_lenient_firstVariableWidth_fails() throws Exception {
796         // fails because literal is a number and variable width parse greedily absorbs it
797         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK);
798         ParsePosition pp = new ParsePosition(0);
799         TemporalAccessor parsed = f.parseUnresolved("12309", pp);
800         assertEquals(pp.getErrorIndex(), 5);
801         assertEquals(parsed, null);
802     }
803 
804     //-----------------------------------------------------------------------
805     @Test
test_adjacent_strict_fractionFollows()806     public void test_adjacent_strict_fractionFollows() throws Exception {
807         // succeeds because hour/min are fixed width
808         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 0, 3, false).toFormatter(Locale.UK);
809         ParsePosition pp = new ParsePosition(0);
810         TemporalAccessor parsed = f.parseUnresolved("1230567", pp);
811         assertEquals(pp.getErrorIndex(), -1);
812         assertEquals(pp.getIndex(), 7);
813         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
814         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
815         assertEquals(parsed.getLong(NANO_OF_SECOND), 567_000_000L);
816     }
817 
818     @Test
test_adjacent_strict_fractionFollows_2digit()819     public void test_adjacent_strict_fractionFollows_2digit() throws Exception {
820         // succeeds because hour/min are fixed width
821         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 0, 3, false).toFormatter(Locale.UK);
822         ParsePosition pp = new ParsePosition(0);
823         TemporalAccessor parsed = f.parseUnresolved("123056", pp);
824         assertEquals(pp.getErrorIndex(), -1);
825         assertEquals(pp.getIndex(), 6);
826         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
827         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
828         assertEquals(parsed.getLong(NANO_OF_SECOND), 560_000_000L);
829     }
830 
831     @Test
test_adjacent_strict_fractionFollows_0digit()832     public void test_adjacent_strict_fractionFollows_0digit() throws Exception {
833         // succeeds because hour/min are fixed width
834         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 0, 3, false).toFormatter(Locale.UK);
835         ParsePosition pp = new ParsePosition(0);
836         TemporalAccessor parsed = f.parseUnresolved("1230", pp);
837         assertEquals(pp.getErrorIndex(), -1);
838         assertEquals(pp.getIndex(), 4);
839         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
840         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
841     }
842 
843     @Test
test_adjacent_lenient_fractionFollows()844     public void test_adjacent_lenient_fractionFollows() throws Exception {
845         // succeeds because hour/min are fixed width
846         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 3, 3, false).toFormatter(Locale.UK);
847         ParsePosition pp = new ParsePosition(0);
848         TemporalAccessor parsed = f.parseUnresolved("1230567", pp);
849         assertEquals(pp.getErrorIndex(), -1);
850         assertEquals(pp.getIndex(), 7);
851         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
852         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
853         assertEquals(parsed.getLong(NANO_OF_SECOND), 567_000_000L);
854     }
855 
856     @Test
test_adjacent_lenient_fractionFollows_2digit()857     public void test_adjacent_lenient_fractionFollows_2digit() throws Exception {
858         // succeeds because hour/min are fixed width
859         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 3, 3, false).toFormatter(Locale.UK);
860         ParsePosition pp = new ParsePosition(0);
861         TemporalAccessor parsed = f.parseUnresolved("123056", pp);
862         assertEquals(pp.getErrorIndex(), -1);
863         assertEquals(pp.getIndex(), 6);
864         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
865         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
866         assertEquals(parsed.getLong(NANO_OF_SECOND), 560_000_000L);
867     }
868 
869     @Test
test_adjacent_lenient_fractionFollows_0digit()870     public void test_adjacent_lenient_fractionFollows_0digit() throws Exception {
871         // succeeds because hour/min are fixed width
872         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 3, 3, false).toFormatter(Locale.UK);
873         ParsePosition pp = new ParsePosition(0);
874         TemporalAccessor parsed = f.parseUnresolved("1230", pp);
875         assertEquals(pp.getErrorIndex(), -1);
876         assertEquals(pp.getIndex(), 4);
877         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
878         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
879     }
880 
881 }
882