1 /*
   2  * Copyright (c) 2012, 2016, 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             {"g"},
 636             {"ggggg"},
 637         };
 638     }
 639 
 640     @Test(dataProvider="validPatterns")
 641     public void test_appendPattern_valid(String input) throws Exception {
 642         builder.appendPattern(input);  // test is for no error here
 643     }
 644 
 645     //-----------------------------------------------------------------------
 646     @DataProvider(name="invalidPatterns")
 647     Object[][] dataInvalid() {
 648         return new Object[][] {
 649             {"'"},
 650             {"'hello"},
 651             {"'hel''lo"},
 652             {"'hello''"},
 653             {"{"},
 654             {"}"},
 655             {"{}"},
 656             {"#"},
 657             {"]"},
 658             {"yyyy]"},
 659             {"yyyy]MM"},
 660             {"yyyy[MM]]"},
 661 
 662             {"aa"},
 663             {"aaa"},
 664             {"aaaa"},
 665             {"aaaaa"},
 666             {"aaaaaa"},
 667             {"MMMMMM"},
 668             {"QQQQQQ"},
 669             {"qqqqqq"},
 670             {"EEEEEE"},
 671             {"eeeeee"},
 672             {"cc"},
 673             {"cccccc"},
 674             {"ddd"},
 675             {"DDDD"},
 676             {"FF"},
 677             {"FFF"},
 678             {"hhh"},
 679             {"HHH"},
 680             {"kkk"},
 681             {"KKK"},
 682             {"mmm"},
 683             {"sss"},
 684             {"OO"},
 685             {"OOO"},
 686             {"OOOOO"},
 687             {"XXXXXX"},
 688             {"zzzzz"},
 689             {"ZZZZZZ"},
 690 
 691             {"RO"},
 692 
 693             {"p"},
 694             {"pp"},
 695             {"p:"},
 696 
 697             {"f"},
 698             {"ff"},
 699             {"f:"},
 700             {"fy"},
 701             {"fa"},
 702             {"fM"},
 703 
 704             {"www"},
 705             {"WW"},
 706         };
 707     }
 708 
 709     @Test(dataProvider="invalidPatterns", expectedExceptions=IllegalArgumentException.class)
 710     public void test_appendPattern_invalid(String input) throws Exception {
 711         builder.appendPattern(input);  // test is for error here
 712     }
 713 
 714     //-----------------------------------------------------------------------
 715     @DataProvider(name="patternPrint")
 716     Object[][] data_patternPrint() {
 717         return new Object[][] {
 718             {"Q", date(2012, 2, 10), "1"},
 719             {"QQ", date(2012, 2, 10), "01"},
 720             {"QQQ", date(2012, 2, 10), "Q1"},
 721             {"QQQQ", date(2012, 2, 10), "1st quarter"},
 722             {"QQQQQ", date(2012, 2, 10), "1"},
 723         };
 724     }
 725 
 726     @Test(dataProvider="patternPrint")
 727     public void test_appendPattern_patternPrint(String input, Temporal temporal, String expected) throws Exception {
 728         DateTimeFormatter f = builder.appendPattern(input).toFormatter(Locale.UK);
 729         String test = f.format(temporal);
 730         assertEquals(test, expected);
 731     }
 732 
 733     private static Temporal date(int y, int m, int d) {
 734         return LocalDate.of(y, m, d);
 735     }
 736 
 737     //-----------------------------------------------------------------------
 738     @DataProvider(name="modJulianFieldPattern")
 739     Object[][] data_modJuilanFieldPattern() {
 740         return new Object[][] {
 741             {"g", "1"},
 742             {"g", "123456"},
 743             {"gggggg", "123456"},
 744         };
 745     }
 746 
 747     @Test(dataProvider="modJulianFieldPattern")
 748     public void test_modJulianFieldPattern(String pattern, String input) throws Exception {
 749         DateTimeFormatter.ofPattern(pattern).parse(input);
 750     }
 751 
 752     @DataProvider(name="modJulianFieldValues")
 753     Object[][] data_modJuilanFieldValues() {
 754         return new Object[][] {
 755             {1970, 1, 1, "40587"},
 756             {1858, 11, 17, "0"},
 757             {1858, 11, 16, "-1"},
 758         };
 759     }
 760 
 761     @Test(dataProvider="modJulianFieldValues")
 762     public void test_modJulianFieldValues(int y, int m, int d, String expected) throws Exception {
 763         DateTimeFormatter df = new DateTimeFormatterBuilder().appendPattern("g").toFormatter();
 764          assertEquals(LocalDate.of(y, m, d).format(df), expected);
 765     }
 766 
 767 
 768     //-----------------------------------------------------------------------
 769     @Test
 770     public void test_adjacent_strict_firstFixedWidth() throws Exception {
 771         // succeeds because both number elements are fixed width
 772         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK);
 773         ParsePosition pp = new ParsePosition(0);
 774         TemporalAccessor parsed = f.parseUnresolved("12309", pp);
 775         assertEquals(pp.getErrorIndex(), -1);
 776         assertEquals(pp.getIndex(), 5);
 777         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
 778         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
 779     }
 780 
 781     @Test
 782     public void test_adjacent_strict_firstVariableWidth_success() throws Exception {
 783         // succeeds greedily parsing variable width, then fixed width, to non-numeric Z
 784         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('Z').toFormatter(Locale.UK);
 785         ParsePosition pp = new ParsePosition(0);
 786         TemporalAccessor parsed = f.parseUnresolved("12309Z", pp);
 787         assertEquals(pp.getErrorIndex(), -1);
 788         assertEquals(pp.getIndex(), 6);
 789         assertEquals(parsed.getLong(HOUR_OF_DAY), 123L);
 790         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 9L);
 791     }
 792 
 793     @Test
 794     public void test_adjacent_strict_firstVariableWidth_fails() throws Exception {
 795         // fails because literal is a number and variable width parse greedily absorbs it
 796         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK);
 797         ParsePosition pp = new ParsePosition(0);
 798         TemporalAccessor parsed = f.parseUnresolved("12309", pp);
 799         assertEquals(pp.getErrorIndex(), 5);
 800         assertEquals(parsed, null);
 801     }
 802 
 803     @Test
 804     public void test_adjacent_lenient() throws Exception {
 805         // succeeds because both number elements are fixed width even in lenient mode
 806         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK);
 807         ParsePosition pp = new ParsePosition(0);
 808         TemporalAccessor parsed = f.parseUnresolved("12309", pp);
 809         assertEquals(pp.getErrorIndex(), -1);
 810         assertEquals(pp.getIndex(), 5);
 811         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
 812         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
 813     }
 814 
 815     @Test
 816     public void test_adjacent_lenient_firstVariableWidth_success() throws Exception {
 817         // succeeds greedily parsing variable width, then fixed width, to non-numeric Z
 818         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('Z').toFormatter(Locale.UK);
 819         ParsePosition pp = new ParsePosition(0);
 820         TemporalAccessor parsed = f.parseUnresolved("12309Z", pp);
 821         assertEquals(pp.getErrorIndex(), -1);
 822         assertEquals(pp.getIndex(), 6);
 823         assertEquals(parsed.getLong(HOUR_OF_DAY), 123L);
 824         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 9L);
 825     }
 826 
 827     @Test
 828     public void test_adjacent_lenient_firstVariableWidth_fails() throws Exception {
 829         // fails because literal is a number and variable width parse greedily absorbs it
 830         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK);
 831         ParsePosition pp = new ParsePosition(0);
 832         TemporalAccessor parsed = f.parseUnresolved("12309", pp);
 833         assertEquals(pp.getErrorIndex(), 5);
 834         assertEquals(parsed, null);
 835     }
 836 
 837     //-----------------------------------------------------------------------
 838     @Test
 839     public void test_adjacent_strict_fractionFollows() throws Exception {
 840         // succeeds because hour/min are fixed width
 841         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 0, 3, false).toFormatter(Locale.UK);
 842         ParsePosition pp = new ParsePosition(0);
 843         TemporalAccessor parsed = f.parseUnresolved("1230567", pp);
 844         assertEquals(pp.getErrorIndex(), -1);
 845         assertEquals(pp.getIndex(), 7);
 846         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
 847         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
 848         assertEquals(parsed.getLong(NANO_OF_SECOND), 567_000_000L);
 849     }
 850 
 851     @Test
 852     public void test_adjacent_strict_fractionFollows_2digit() throws Exception {
 853         // succeeds because hour/min are fixed width
 854         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 0, 3, false).toFormatter(Locale.UK);
 855         ParsePosition pp = new ParsePosition(0);
 856         TemporalAccessor parsed = f.parseUnresolved("123056", pp);
 857         assertEquals(pp.getErrorIndex(), -1);
 858         assertEquals(pp.getIndex(), 6);
 859         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
 860         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
 861         assertEquals(parsed.getLong(NANO_OF_SECOND), 560_000_000L);
 862     }
 863 
 864     @Test
 865     public void test_adjacent_strict_fractionFollows_0digit() throws Exception {
 866         // succeeds because hour/min are fixed width
 867         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 0, 3, false).toFormatter(Locale.UK);
 868         ParsePosition pp = new ParsePosition(0);
 869         TemporalAccessor parsed = f.parseUnresolved("1230", pp);
 870         assertEquals(pp.getErrorIndex(), -1);
 871         assertEquals(pp.getIndex(), 4);
 872         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
 873         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
 874     }
 875 
 876     @Test
 877     public void test_adjacent_lenient_fractionFollows() throws Exception {
 878         // succeeds because hour/min are fixed width
 879         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 3, 3, false).toFormatter(Locale.UK);
 880         ParsePosition pp = new ParsePosition(0);
 881         TemporalAccessor parsed = f.parseUnresolved("1230567", pp);
 882         assertEquals(pp.getErrorIndex(), -1);
 883         assertEquals(pp.getIndex(), 7);
 884         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
 885         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
 886         assertEquals(parsed.getLong(NANO_OF_SECOND), 567_000_000L);
 887     }
 888 
 889     @Test
 890     public void test_adjacent_lenient_fractionFollows_2digit() throws Exception {
 891         // succeeds because hour/min are fixed width
 892         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 3, 3, false).toFormatter(Locale.UK);
 893         ParsePosition pp = new ParsePosition(0);
 894         TemporalAccessor parsed = f.parseUnresolved("123056", pp);
 895         assertEquals(pp.getErrorIndex(), -1);
 896         assertEquals(pp.getIndex(), 6);
 897         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
 898         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
 899         assertEquals(parsed.getLong(NANO_OF_SECOND), 560_000_000L);
 900     }
 901 
 902     @Test
 903     public void test_adjacent_lenient_fractionFollows_0digit() throws Exception {
 904         // succeeds because hour/min are fixed width
 905         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 3, 3, false).toFormatter(Locale.UK);
 906         ParsePosition pp = new ParsePosition(0);
 907         TemporalAccessor parsed = f.parseUnresolved("1230", pp);
 908         assertEquals(pp.getErrorIndex(), -1);
 909         assertEquals(pp.getIndex(), 4);
 910         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
 911         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
 912     }
 913 
 914 }