Home | History | Annotate | Download | only in format
      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
     97     public void setUp() {
     98         builder = new DateTimeFormatterBuilder();
     99     }
    100 
    101     //-----------------------------------------------------------------------
    102     @Test
    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
    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
    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)
    133     public void test_parseDefaulting_null() {
    134         builder.parseDefaulting(null, 1);
    135     }
    136 
    137     //-----------------------------------------------------------------------
    138     @Test(expectedExceptions=NullPointerException.class)
    139     public void test_appendValue_1arg_null() throws Exception {
    140         builder.appendValue(null);
    141     }
    142 
    143     //-----------------------------------------------------------------------
    144     @Test(expectedExceptions=NullPointerException.class)
    145     public void test_appendValue_2arg_null() throws Exception {
    146         builder.appendValue(null, 3);
    147     }
    148 
    149     @Test(expectedExceptions=IllegalArgumentException.class)
    150     public void test_appendValue_2arg_widthTooSmall() throws Exception {
    151         builder.appendValue(DAY_OF_MONTH, 0);
    152     }
    153 
    154     @Test(expectedExceptions=IllegalArgumentException.class)
    155     public void test_appendValue_2arg_widthTooBig() throws Exception {
    156         builder.appendValue(DAY_OF_MONTH, 20);
    157     }
    158 
    159     //-----------------------------------------------------------------------
    160     @Test(expectedExceptions=NullPointerException.class)
    161     public void test_appendValue_3arg_nullField() throws Exception {
    162         builder.appendValue(null, 2, 3, SignStyle.NORMAL);
    163     }
    164 
    165     @Test(expectedExceptions=IllegalArgumentException.class)
    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)
    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)
    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)
    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)
    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)
    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)
    197     public void test_appendValueReduced_int_nullField() throws Exception {
    198         builder.appendValueReduced(null, 2, 2, 2000);
    199     }
    200 
    201     @Test(expectedExceptions=IllegalArgumentException.class)
    202     public void test_appendValueReduced_int_minWidthTooSmall() throws Exception {
    203         builder.appendValueReduced(YEAR, 0, 2, 2000);
    204     }
    205 
    206     @Test(expectedExceptions=IllegalArgumentException.class)
    207     public void test_appendValueReduced_int_minWidthTooBig() throws Exception {
    208         builder.appendValueReduced(YEAR, 11, 2, 2000);
    209     }
    210 
    211     @Test(expectedExceptions=IllegalArgumentException.class)
    212     public void test_appendValueReduced_int_maxWidthTooSmall() throws Exception {
    213         builder.appendValueReduced(YEAR, 2, 0, 2000);
    214     }
    215 
    216     @Test(expectedExceptions=IllegalArgumentException.class)
    217     public void test_appendValueReduced_int_maxWidthTooBig() throws Exception {
    218         builder.appendValueReduced(YEAR, 2, 11, 2000);
    219     }
    220 
    221     @Test(expectedExceptions=IllegalArgumentException.class)
    222     public void test_appendValueReduced_int_maxWidthLessThanMin() throws Exception {
    223         builder.appendValueReduced(YEAR, 2, 1, 2000);
    224     }
    225 
    226     //-----------------------------------------------------------------------
    227     @Test(expectedExceptions=NullPointerException.class)
    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)
    233     public void test_appendValueReduced_date_nullDate() throws Exception {
    234         builder.appendValueReduced(YEAR, 2, 2, null);
    235     }
    236 
    237     @Test(expectedExceptions=IllegalArgumentException.class)
    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)
    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)
    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)
    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)
    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)
    266     public void test_appendFraction_4arg_nullRule() throws Exception {
    267         builder.appendFraction(null, 1, 9, false);
    268     }
    269 
    270     @Test(expectedExceptions=IllegalArgumentException.class)
    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)
    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)
    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)
    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)
    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)
    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)
    304     public void test_appendText_1arg_null() throws Exception {
    305         builder.appendText(null);
    306     }
    307 
    308     //-----------------------------------------------------------------------
    309     @Test(expectedExceptions=NullPointerException.class)
    310     public void test_appendText_2arg_nullRule() throws Exception {
    311         builder.appendText(null, TextStyle.SHORT);
    312     }
    313 
    314     @Test(expectedExceptions=NullPointerException.class)
    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)
    321     public void test_appendTextMap_nullRule() throws Exception {
    322         builder.appendText(null, new HashMap<>());
    323     }
    324 
    325     @Test(expectedExceptions=NullPointerException.class)
    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")
    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")
    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")
    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")
    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)
    414     public void test_appendOffset_badPattern(String pattern) throws Exception {
    415         builder.appendOffset(pattern, "Z");
    416     }
    417 
    418     @Test(expectedExceptions=NullPointerException.class)
    419     public void test_appendOffset_3arg_nullText() throws Exception {
    420         builder.appendOffset("+HH:MM", null);
    421     }
    422 
    423     @Test(expectedExceptions=NullPointerException.class)
    424     public void test_appendOffset_3arg_nullPattern() throws Exception {
    425         builder.appendOffset(null, "Z");
    426     }
    427 
    428     //-----------------------------------------------------------------------
    429     //-----------------------------------------------------------------------
    430     //-----------------------------------------------------------------------
    431     @Test(expectedExceptions=NullPointerException.class)
    432     public void test_appendZoneText_1arg_nullText() throws Exception {
    433         builder.appendZoneText(null);
    434     }
    435 
    436     //-----------------------------------------------------------------------
    437     //-----------------------------------------------------------------------
    438     //-----------------------------------------------------------------------
    439     @Test
    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)
    446     public void test_padNext_1arg_invalidWidth() throws Exception {
    447         builder.padNext(0);
    448     }
    449 
    450     //-----------------------------------------------------------------------
    451     @Test
    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)
    458     public void test_padNext_2arg_invalidWidth() throws Exception {
    459         builder.padNext(0, '-');
    460     }
    461 
    462     //-----------------------------------------------------------------------
    463     @Test
    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)
    476     public void test_optionalEnd_noStart() throws Exception {
    477         builder.optionalEnd();
    478     }
    479 
    480     //-----------------------------------------------------------------------
    481     //-----------------------------------------------------------------------
    482     //-----------------------------------------------------------------------
    483     @DataProvider(name="validPatterns")
    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")
    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")
    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)
    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")
    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")
    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 
    731     private static Temporal date(int y, int m, int d) {
    732         return LocalDate.of(y, m, d);
    733     }
    734 
    735     //-----------------------------------------------------------------------
    736     @Test
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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