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.format.DateTimeFormatter.BASIC_ISO_DATE;
  63 import static java.time.temporal.ChronoField.DAY_OF_MONTH;
  64 import static java.time.temporal.ChronoField.HOUR_OF_DAY;
  65 import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
  66 import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
  67 import static java.time.temporal.ChronoField.NANO_OF_SECOND;
  68 import static java.time.temporal.ChronoField.OFFSET_SECONDS;
  69 import static java.time.temporal.ChronoField.YEAR;
  70 import static org.testng.Assert.assertEquals;
  71 
  72 import java.text.ParsePosition;
  73 import java.time.LocalDate;
  74 import java.time.LocalDateTime;
  75 import java.time.LocalTime;
  76 import java.time.Month;
  77 import java.time.YearMonth;
  78 import java.time.ZonedDateTime;
  79 import java.time.ZoneId;
  80 import java.time.ZoneOffset;
  81 import java.time.format.DateTimeFormatter;
  82 import java.time.format.DateTimeFormatterBuilder;
  83 import java.time.format.DateTimeParseException;
  84 import java.time.format.SignStyle;
  85 import java.time.format.TextStyle;
  86 import java.time.temporal.Temporal;
  87 import java.time.temporal.TemporalAccessor;
  88 import java.util.HashMap;
  89 import java.util.Locale;
  90 import java.util.Map;
  91 
  92 import org.testng.annotations.BeforeMethod;
  93 import org.testng.annotations.DataProvider;
  94 import org.testng.annotations.Test;
  95 
  96 /**
  97  * Test DateTimeFormatterBuilder.
  98  */
  99 @Test
 100 public class TCKDateTimeFormatterBuilder {
 101 
 102     private DateTimeFormatterBuilder builder;
 103 
 104     @BeforeMethod
 105     public void setUp() {
 106         builder = new DateTimeFormatterBuilder();
 107     }
 108 
 109     //-----------------------------------------------------------------------
 110     @Test
 111     public void test_toFormatter_empty() throws Exception {
 112         DateTimeFormatter f = builder.toFormatter();
 113         assertEquals(f.format(LocalDate.of(2012, 6, 30)), "");
 114     }
 115 
 116     //-----------------------------------------------------------------------
 117     @Test
 118     public void test_parseDefaulting_entireDate() {
 119         DateTimeFormatter f = builder
 120             .parseDefaulting(YEAR, 2012).parseDefaulting(MONTH_OF_YEAR, 6)
 121             .parseDefaulting(DAY_OF_MONTH, 30).toFormatter();
 122         LocalDate parsed = f.parse("", LocalDate::from);  // blank string can be parsed
 123         assertEquals(parsed, LocalDate.of(2012, 6, 30));
 124     }
 125 
 126     @Test
 127     public void test_parseDefaulting_yearOptionalMonthOptionalDay() {
 128         DateTimeFormatter f = builder
 129                 .appendValue(YEAR)
 130                 .optionalStart().appendLiteral('-').appendValue(MONTH_OF_YEAR)
 131                 .optionalStart().appendLiteral('-').appendValue(DAY_OF_MONTH)
 132                 .optionalEnd().optionalEnd()
 133                 .parseDefaulting(MONTH_OF_YEAR, 1)
 134                 .parseDefaulting(DAY_OF_MONTH, 1).toFormatter();
 135         assertEquals(f.parse("2012", LocalDate::from), LocalDate.of(2012, 1, 1));
 136         assertEquals(f.parse("2012-6", LocalDate::from), LocalDate.of(2012, 6, 1));
 137         assertEquals(f.parse("2012-6-30", LocalDate::from), LocalDate.of(2012, 6, 30));
 138     }
 139 
 140     @Test(expectedExceptions = NullPointerException.class)
 141     public void test_parseDefaulting_null() {
 142         builder.parseDefaulting(null, 1);
 143     }
 144 
 145     //-----------------------------------------------------------------------
 146     @Test(expectedExceptions=NullPointerException.class)
 147     public void test_appendValue_1arg_null() throws Exception {
 148         builder.appendValue(null);
 149     }
 150 
 151     //-----------------------------------------------------------------------
 152     @Test(expectedExceptions=NullPointerException.class)
 153     public void test_appendValue_2arg_null() throws Exception {
 154         builder.appendValue(null, 3);
 155     }
 156 
 157     @Test(expectedExceptions=IllegalArgumentException.class)
 158     public void test_appendValue_2arg_widthTooSmall() throws Exception {
 159         builder.appendValue(DAY_OF_MONTH, 0);
 160     }
 161 
 162     @Test(expectedExceptions=IllegalArgumentException.class)
 163     public void test_appendValue_2arg_widthTooBig() throws Exception {
 164         builder.appendValue(DAY_OF_MONTH, 20);
 165     }
 166 
 167     //-----------------------------------------------------------------------
 168     @Test(expectedExceptions=NullPointerException.class)
 169     public void test_appendValue_3arg_nullField() throws Exception {
 170         builder.appendValue(null, 2, 3, SignStyle.NORMAL);
 171     }
 172 
 173     @Test(expectedExceptions=IllegalArgumentException.class)
 174     public void test_appendValue_3arg_minWidthTooSmall() throws Exception {
 175         builder.appendValue(DAY_OF_MONTH, 0, 2, SignStyle.NORMAL);
 176     }
 177 
 178     @Test(expectedExceptions=IllegalArgumentException.class)
 179     public void test_appendValue_3arg_minWidthTooBig() throws Exception {
 180         builder.appendValue(DAY_OF_MONTH, 20, 2, SignStyle.NORMAL);
 181     }
 182 
 183     @Test(expectedExceptions=IllegalArgumentException.class)
 184     public void test_appendValue_3arg_maxWidthTooSmall() throws Exception {
 185         builder.appendValue(DAY_OF_MONTH, 2, 0, SignStyle.NORMAL);
 186     }
 187 
 188     @Test(expectedExceptions=IllegalArgumentException.class)
 189     public void test_appendValue_3arg_maxWidthTooBig() throws Exception {
 190         builder.appendValue(DAY_OF_MONTH, 2, 20, SignStyle.NORMAL);
 191     }
 192 
 193     @Test(expectedExceptions=IllegalArgumentException.class)
 194     public void test_appendValue_3arg_maxWidthMinWidth() throws Exception {
 195         builder.appendValue(DAY_OF_MONTH, 4, 2, SignStyle.NORMAL);
 196     }
 197 
 198     @Test(expectedExceptions=NullPointerException.class)
 199     public void test_appendValue_3arg_nullSignStyle() throws Exception {
 200         builder.appendValue(DAY_OF_MONTH, 2, 3, null);
 201     }
 202 
 203     //-----------------------------------------------------------------------
 204     @Test(expectedExceptions=NullPointerException.class)
 205     public void test_appendValueReduced_int_nullField() throws Exception {
 206         builder.appendValueReduced(null, 2, 2, 2000);
 207     }
 208 
 209     @Test(expectedExceptions=IllegalArgumentException.class)
 210     public void test_appendValueReduced_int_minWidthTooSmall() throws Exception {
 211         builder.appendValueReduced(YEAR, 0, 2, 2000);
 212     }
 213 
 214     @Test(expectedExceptions=IllegalArgumentException.class)
 215     public void test_appendValueReduced_int_minWidthTooBig() throws Exception {
 216         builder.appendValueReduced(YEAR, 11, 2, 2000);
 217     }
 218 
 219     @Test(expectedExceptions=IllegalArgumentException.class)
 220     public void test_appendValueReduced_int_maxWidthTooSmall() throws Exception {
 221         builder.appendValueReduced(YEAR, 2, 0, 2000);
 222     }
 223 
 224     @Test(expectedExceptions=IllegalArgumentException.class)
 225     public void test_appendValueReduced_int_maxWidthTooBig() throws Exception {
 226         builder.appendValueReduced(YEAR, 2, 11, 2000);
 227     }
 228 
 229     @Test(expectedExceptions=IllegalArgumentException.class)
 230     public void test_appendValueReduced_int_maxWidthLessThanMin() throws Exception {
 231         builder.appendValueReduced(YEAR, 2, 1, 2000);
 232     }
 233 
 234     //-----------------------------------------------------------------------
 235     @Test(expectedExceptions=NullPointerException.class)
 236     public void test_appendValueReduced_date_nullField() throws Exception {
 237         builder.appendValueReduced(null, 2, 2, LocalDate.of(2000, 1, 1));
 238     }
 239 
 240     @Test(expectedExceptions=NullPointerException.class)
 241     public void test_appendValueReduced_date_nullDate() throws Exception {
 242         builder.appendValueReduced(YEAR, 2, 2, null);
 243     }
 244 
 245     @Test(expectedExceptions=IllegalArgumentException.class)
 246     public void test_appendValueReduced_date_minWidthTooSmall() throws Exception {
 247         builder.appendValueReduced(YEAR, 0, 2, LocalDate.of(2000, 1, 1));
 248     }
 249 
 250     @Test(expectedExceptions=IllegalArgumentException.class)
 251     public void test_appendValueReduced_date_minWidthTooBig() throws Exception {
 252         builder.appendValueReduced(YEAR, 11, 2, LocalDate.of(2000, 1, 1));
 253     }
 254 
 255     @Test(expectedExceptions=IllegalArgumentException.class)
 256     public void test_appendValueReduced_date_maxWidthTooSmall() throws Exception {
 257         builder.appendValueReduced(YEAR, 2, 0, LocalDate.of(2000, 1, 1));
 258     }
 259 
 260     @Test(expectedExceptions=IllegalArgumentException.class)
 261     public void test_appendValueReduced_date_maxWidthTooBig() throws Exception {
 262         builder.appendValueReduced(YEAR, 2, 11, LocalDate.of(2000, 1, 1));
 263     }
 264 
 265     @Test(expectedExceptions=IllegalArgumentException.class)
 266     public void test_appendValueReduced_date_maxWidthLessThanMin() throws Exception {
 267         builder.appendValueReduced(YEAR, 2, 1, LocalDate.of(2000, 1, 1));
 268     }
 269 
 270     //-----------------------------------------------------------------------
 271     //-----------------------------------------------------------------------
 272     //-----------------------------------------------------------------------
 273     @Test(expectedExceptions=NullPointerException.class)
 274     public void test_appendFraction_4arg_nullRule() throws Exception {
 275         builder.appendFraction(null, 1, 9, false);
 276     }
 277 
 278     @Test(expectedExceptions=IllegalArgumentException.class)
 279     public void test_appendFraction_4arg_invalidRuleNotFixedSet() throws Exception {
 280         builder.appendFraction(DAY_OF_MONTH, 1, 9, false);
 281     }
 282 
 283     @Test(expectedExceptions=IllegalArgumentException.class)
 284     public void test_appendFraction_4arg_minTooSmall() throws Exception {
 285         builder.appendFraction(MINUTE_OF_HOUR, -1, 9, false);
 286     }
 287 
 288     @Test(expectedExceptions=IllegalArgumentException.class)
 289     public void test_appendFraction_4arg_minTooBig() throws Exception {
 290         builder.appendFraction(MINUTE_OF_HOUR, 10, 9, false);
 291     }
 292 
 293     @Test(expectedExceptions=IllegalArgumentException.class)
 294     public void test_appendFraction_4arg_maxTooSmall() throws Exception {
 295         builder.appendFraction(MINUTE_OF_HOUR, 0, -1, false);
 296     }
 297 
 298     @Test(expectedExceptions=IllegalArgumentException.class)
 299     public void test_appendFraction_4arg_maxTooBig() throws Exception {
 300         builder.appendFraction(MINUTE_OF_HOUR, 1, 10, false);
 301     }
 302 
 303     @Test(expectedExceptions=IllegalArgumentException.class)
 304     public void test_appendFraction_4arg_maxWidthMinWidth() throws Exception {
 305         builder.appendFraction(MINUTE_OF_HOUR, 9, 3, false);
 306     }
 307 
 308     //-----------------------------------------------------------------------
 309     //-----------------------------------------------------------------------
 310     //-----------------------------------------------------------------------
 311     @Test(expectedExceptions=NullPointerException.class)
 312     public void test_appendText_1arg_null() throws Exception {
 313         builder.appendText(null);
 314     }
 315 
 316     //-----------------------------------------------------------------------
 317     @Test(expectedExceptions=NullPointerException.class)
 318     public void test_appendText_2arg_nullRule() throws Exception {
 319         builder.appendText(null, TextStyle.SHORT);
 320     }
 321 
 322     @Test(expectedExceptions=NullPointerException.class)
 323     public void test_appendText_2arg_nullStyle() throws Exception {
 324         builder.appendText(MONTH_OF_YEAR, (TextStyle) null);
 325     }
 326 
 327     //-----------------------------------------------------------------------
 328     @Test(expectedExceptions=NullPointerException.class)
 329     public void test_appendTextMap_nullRule() throws Exception {
 330         builder.appendText(null, new HashMap<>());
 331     }
 332 
 333     @Test(expectedExceptions=NullPointerException.class)
 334     public void test_appendTextMap_nullStyle() throws Exception {
 335         builder.appendText(MONTH_OF_YEAR, (Map<Long, String>) null);
 336     }
 337 
 338     //-----------------------------------------------------------------------
 339     //-----------------------------------------------------------------------
 340     //-----------------------------------------------------------------------
 341     @DataProvider(name="offsetPatterns")
 342     Object[][] data_offsetPatterns() {
 343         return new Object[][] {
 344                 {"+HH", 2, 0, 0, "+02"},
 345                 {"+HH", -2, 0, 0, "-02"},
 346                 {"+HH", 2, 30, 0, "+02"},
 347                 {"+HH", 2, 0, 45, "+02"},
 348                 {"+HH", 2, 30, 45, "+02"},
 349 
 350                 {"+HHmm", 2, 0, 0, "+02"},
 351                 {"+HHmm", -2, 0, 0, "-02"},
 352                 {"+HHmm", 2, 30, 0, "+0230"},
 353                 {"+HHmm", 2, 0, 45, "+02"},
 354                 {"+HHmm", 2, 30, 45, "+0230"},
 355 
 356                 {"+HH:mm", 2, 0, 0, "+02"},
 357                 {"+HH:mm", -2, 0, 0, "-02"},
 358                 {"+HH:mm", 2, 30, 0, "+02:30"},
 359                 {"+HH:mm", 2, 0, 45, "+02"},
 360                 {"+HH:mm", 2, 30, 45, "+02:30"},
 361 
 362                 {"+HHMM", 2, 0, 0, "+0200"},
 363                 {"+HHMM", -2, 0, 0, "-0200"},
 364                 {"+HHMM", 2, 30, 0, "+0230"},
 365                 {"+HHMM", 2, 0, 45, "+0200"},
 366                 {"+HHMM", 2, 30, 45, "+0230"},
 367 
 368                 {"+HH:MM", 2, 0, 0, "+02:00"},
 369                 {"+HH:MM", -2, 0, 0, "-02:00"},
 370                 {"+HH:MM", 2, 30, 0, "+02:30"},
 371                 {"+HH:MM", 2, 0, 45, "+02:00"},
 372                 {"+HH:MM", 2, 30, 45, "+02:30"},
 373 
 374                 {"+HHMMss", 2, 0, 0, "+0200"},
 375                 {"+HHMMss", -2, 0, 0, "-0200"},
 376                 {"+HHMMss", 2, 30, 0, "+0230"},
 377                 {"+HHMMss", 2, 0, 45, "+020045"},
 378                 {"+HHMMss", 2, 30, 45, "+023045"},
 379 
 380                 {"+HH:MM:ss", 2, 0, 0, "+02:00"},
 381                 {"+HH:MM:ss", -2, 0, 0, "-02:00"},
 382                 {"+HH:MM:ss", 2, 30, 0, "+02:30"},
 383                 {"+HH:MM:ss", 2, 0, 45, "+02:00:45"},
 384                 {"+HH:MM:ss", 2, 30, 45, "+02:30:45"},
 385 
 386                 {"+HHMMSS", 2, 0, 0, "+020000"},
 387                 {"+HHMMSS", -2, 0, 0, "-020000"},
 388                 {"+HHMMSS", 2, 30, 0, "+023000"},
 389                 {"+HHMMSS", 2, 0, 45, "+020045"},
 390                 {"+HHMMSS", 2, 30, 45, "+023045"},
 391 
 392                 {"+HH:MM:SS", 2, 0, 0, "+02:00:00"},
 393                 {"+HH:MM:SS", -2, 0, 0, "-02:00:00"},
 394                 {"+HH:MM:SS", 2, 30, 0, "+02:30:00"},
 395                 {"+HH:MM:SS", 2, 0, 45, "+02:00:45"},
 396                 {"+HH:MM:SS", 2, 30, 45, "+02:30:45"},
 397 
 398                 {"+HHmmss", 2, 0, 0, "+02"},
 399                 {"+HHmmss", -2, 0, 0, "-02"},
 400                 {"+HHmmss", 2, 30, 0, "+0230"},
 401                 {"+HHmmss", 2, 0, 45, "+020045"},
 402                 {"+HHmmss", 2, 30, 45, "+023045"},
 403 
 404                 {"+HH:mm:ss", 2, 0, 0, "+02"},
 405                 {"+HH:mm:ss", -2, 0, 0, "-02"},
 406                 {"+HH:mm:ss", 2, 30, 0, "+02:30"},
 407                 {"+HH:mm:ss", 2, 0, 45, "+02:00:45"},
 408                 {"+HH:mm:ss", 2, 30, 45, "+02:30:45"},
 409 
 410 
 411         };
 412     }
 413 
 414     @Test(dataProvider="offsetPatterns")
 415     public void test_appendOffset_format(String pattern, int h, int m, int s, String expected) throws Exception {
 416         builder.appendOffset(pattern, "Z");
 417         DateTimeFormatter f = builder.toFormatter();
 418         ZoneOffset offset = ZoneOffset.ofHoursMinutesSeconds(h, m, s);
 419         assertEquals(f.format(offset), expected);
 420     }
 421 
 422     @Test(dataProvider="offsetPatterns")
 423     public void test_appendOffset_parse(String pattern, int h, int m, int s, String expected) throws Exception {
 424         builder.appendOffset(pattern, "Z");
 425         DateTimeFormatter f = builder.toFormatter();
 426         ZoneOffset parsed = f.parse(expected, ZoneOffset::from);
 427         assertEquals(f.format(parsed), expected);
 428     }
 429 
 430     @DataProvider(name="badOffsetPatterns")
 431     Object[][] data_badOffsetPatterns() {
 432         return new Object[][] {
 433             {"HH"},
 434             {"HHMM"},
 435             {"HH:MM"},
 436             {"HHMMss"},
 437             {"HH:MM:ss"},
 438             {"HHMMSS"},
 439             {"HH:MM:SS"},
 440             {"+H"},
 441             {"+HMM"},
 442             {"+HHM"},
 443             {"+A"},
 444         };
 445     }
 446 
 447     @Test(dataProvider="badOffsetPatterns", expectedExceptions=IllegalArgumentException.class)
 448     public void test_appendOffset_badPattern(String pattern) throws Exception {
 449         builder.appendOffset(pattern, "Z");
 450     }
 451 
 452     @Test(expectedExceptions=NullPointerException.class)
 453     public void test_appendOffset_3arg_nullText() throws Exception {
 454         builder.appendOffset("+HH:MM", null);
 455     }
 456 
 457     @Test(expectedExceptions=NullPointerException.class)
 458     public void test_appendOffset_3arg_nullPattern() throws Exception {
 459         builder.appendOffset(null, "Z");
 460     }
 461 
 462     //-----------------------------------------------------------------------
 463     //-----------------------------------------------------------------------
 464     //-----------------------------------------------------------------------
 465     @DataProvider(name = "formatGenericTimeZonePatterns")
 466     Object[][] data_formatGenericNonLocationPatterns() {
 467         return new Object[][] {
 468                 {"v", "America/Los_Angeles", "PT"},
 469                 {"vvvv", "America/Los_Angeles", "Pacific Time"},
 470                 {"v", "America/New_York", "ET"},
 471                 {"vvvv", "America/New_York", "Eastern Time"},
 472         };
 473     }
 474 
 475     @Test(dataProvider = "formatGenericTimeZonePatterns")
 476     public void test_appendZoneText_formatGenericTimeZonePatterns(String pattern, String input, String expected) {
 477         ZonedDateTime zdt = ZonedDateTime.of(LocalDateTime.now(), ZoneId.of(input));
 478         DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern);
 479         assertEquals(zdt.format(df), expected);
 480     }
 481 
 482     @DataProvider(name = "parseGenericTimeZonePatterns")
 483     Object[][]  data_parseGenericTimeZonePatterns() {
 484         return new Object[][] {
 485                 {"yyyy DDD HH mm v", LocalDateTime.of(2015, Month.MARCH, 10, 12, 13), ZoneId.of("America/Los_Angeles"),
 486                  "2015 069 12 13 PT"},
 487                 {"yyyy DDD HH mm vvvv", LocalDateTime.of(2015, Month.MARCH, 10, 12, 13), ZoneId.of("America/Los_Angeles"),
 488                  "2015 069 12 13 Pacific Time"},
 489                 {"yyyy DDD HH mm v", LocalDateTime.of(2015, Month.NOVEMBER, 10, 12, 13), ZoneId.of("America/Los_Angeles"),
 490                  "2015 314 12 13 PT"},
 491                 {"yyyy DDD HH mm vvvv", LocalDateTime.of(2015, Month.NOVEMBER, 10, 12, 13), ZoneId.of("America/Los_Angeles"),
 492                  "2015 314 12 13 Pacific Time"},
 493         };
 494     }
 495 
 496     @Test(dataProvider = "parseGenericTimeZonePatterns")
 497     public void test_appendZoneText_parseGenericTimeZonePatterns(String pattern, LocalDateTime ldt, ZoneId zId, String input) {
 498         DateTimeFormatter df = new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter();
 499         ZonedDateTime expected = ZonedDateTime.parse(input, df);
 500         ZonedDateTime actual = ZonedDateTime.of(ldt, zId);
 501         assertEquals(actual, expected);
 502     }
 503 
 504     @DataProvider(name = "formatNonGenericTimeZonePatterns_1")
 505     Object[][]  data_formatNonGenericTimeZonePatterns_1() {
 506         return new Object[][] {
 507                 {"yyyy-MM-dd HH:mm:ss z", LocalDateTime.of(2015, Month.NOVEMBER, 1, 0, 30),
 508                  "2015-11-01 00:30:00 PDT"},
 509                 {"yyyy-MM-dd HH:mm:ss z", LocalDateTime.of(2015, Month.NOVEMBER, 1, 1, 30),
 510                  "2015-11-01 01:30:00 PDT"},
 511                 {"yyyy-MM-dd HH:mm:ss z", LocalDateTime.of(2015, Month.NOVEMBER, 1, 2, 30),
 512                  "2015-11-01 02:30:00 PST"},
 513                 {"yyyy-MM-dd HH:mm:ss zzzz", LocalDateTime.of(2015, Month.NOVEMBER, 1, 0, 30),
 514                  "2015-11-01 00:30:00 Pacific Daylight Time"},
 515                 {"yyyy-MM-dd HH:mm:ss zzzz", LocalDateTime.of(2015, Month.NOVEMBER, 1, 1, 30),
 516                  "2015-11-01 01:30:00 Pacific Daylight Time"},
 517                 {"yyyy-MM-dd HH:mm:ss zzzz", LocalDateTime.of(2015, Month.NOVEMBER, 1, 2, 30),
 518                  "2015-11-01 02:30:00 Pacific Standard Time"},
 519         };
 520     }
 521 
 522     @Test(dataProvider = "formatNonGenericTimeZonePatterns_1")
 523     public void test_appendZoneText_parseNonGenricTimeZonePatterns_1(String pattern, LocalDateTime ldt, String expected) {
 524         ZoneId  zId = ZoneId.of("America/Los_Angeles");
 525         DateTimeFormatter df = new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter();
 526         ZonedDateTime zdt = ZonedDateTime.of(ldt, zId);
 527         String actual = df.format(zdt);
 528         assertEquals(actual, expected);
 529     }
 530 
 531     @DataProvider(name = "formatNonGenericTimeZonePatterns_2")
 532     Object[][]  data_formatNonGenericTimeZonePatterns_2() {
 533         return new Object[][] {
 534                 {"yyyy-MM-dd HH:mm:ss z", LocalDateTime.of(2015, Month.NOVEMBER, 1, 0, 30),
 535                  "2015-11-01 00:30:00 PDT"},
 536                 {"yyyy-MM-dd HH:mm:ss z", LocalDateTime.of(2015, Month.NOVEMBER, 1, 1, 30),
 537                  "2015-11-01 01:30:00 PT"},
 538                 {"yyyy-MM-dd HH:mm:ss z", LocalDateTime.of(2015, Month.NOVEMBER, 1, 2, 30),
 539                  "2015-11-01 02:30:00 PST"},
 540                 {"yyyy-MM-dd HH:mm:ss zzzz", LocalDateTime.of(2015, Month.NOVEMBER, 1, 0, 30),
 541                  "2015-11-01 00:30:00 Pacific Daylight Time"},
 542                 {"yyyy-MM-dd HH:mm:ss zzzz", LocalDateTime.of(2015, Month.NOVEMBER, 1, 1, 30),
 543                  "2015-11-01 01:30:00 Pacific Time"},
 544                 {"yyyy-MM-dd HH:mm:ss zzzz", LocalDateTime.of(2015, Month.NOVEMBER, 1, 2, 30),
 545                  "2015-11-01 02:30:00 Pacific Standard Time"},
 546         };
 547     }
 548 
 549     @Test(dataProvider = "formatNonGenericTimeZonePatterns_2")
 550     public void test_appendZoneText_parseNonGenricTimeZonePatterns_2(String pattern, LocalDateTime ldt, String expected) {
 551         ZoneId  zId = ZoneId.of("America/Los_Angeles");
 552         DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern).withZone(zId);
 553         String actual = df.format(ldt);
 554         assertEquals(actual, expected);
 555     }
 556 
 557     @Test(expectedExceptions=NullPointerException.class)
 558     public void test_appendZoneText_1arg_nullText() throws Exception {
 559         builder.appendZoneText(null);
 560     }
 561 
 562     //-----------------------------------------------------------------------
 563     //-----------------------------------------------------------------------
 564     //-----------------------------------------------------------------------
 565     @Test
 566     public void test_padNext_1arg() {
 567         builder.appendValue(MONTH_OF_YEAR).appendLiteral(':').padNext(2).appendValue(DAY_OF_MONTH);
 568         assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2: 1");
 569     }
 570 
 571     @Test(expectedExceptions=IllegalArgumentException.class)
 572     public void test_padNext_1arg_invalidWidth() throws Exception {
 573         builder.padNext(0);
 574     }
 575 
 576     //-----------------------------------------------------------------------
 577     @Test
 578     public void test_padNext_2arg_dash() throws Exception {
 579         builder.appendValue(MONTH_OF_YEAR).appendLiteral(':').padNext(2, '-').appendValue(DAY_OF_MONTH);
 580         assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2:-1");
 581     }
 582 
 583     @Test(expectedExceptions=IllegalArgumentException.class)
 584     public void test_padNext_2arg_invalidWidth() throws Exception {
 585         builder.padNext(0, '-');
 586     }
 587 
 588     //-----------------------------------------------------------------------
 589     @Test
 590     public void test_padOptional() throws Exception {
 591         builder.appendValue(MONTH_OF_YEAR).appendLiteral(':')
 592                 .padNext(5).optionalStart().appendValue(DAY_OF_MONTH).optionalEnd()
 593                 .appendLiteral(':').appendValue(YEAR);
 594         assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2:    1:2013");
 595         assertEquals(builder.toFormatter().format(YearMonth.of(2013, 2)), "2:     :2013");
 596     }
 597 
 598     //-----------------------------------------------------------------------
 599     //-----------------------------------------------------------------------
 600     //-----------------------------------------------------------------------
 601     @Test(expectedExceptions=IllegalStateException.class)
 602     public void test_optionalEnd_noStart() throws Exception {
 603         builder.optionalEnd();
 604     }
 605 
 606     //-----------------------------------------------------------------------
 607     //-----------------------------------------------------------------------
 608     //-----------------------------------------------------------------------
 609     @DataProvider(name="validPatterns")
 610     Object[][] dataValid() {
 611         return new Object[][] {
 612             {"'a'"},
 613             {"''"},
 614             {"'!'"},
 615             {"!"},
 616             {"'#'"},
 617 
 618             {"'hello_people,][)('"},
 619             {"'hi'"},
 620             {"'yyyy'"},
 621             {"''''"},
 622             {"'o''clock'"},
 623 
 624             {"G"},
 625             {"GG"},
 626             {"GGG"},
 627             {"GGGG"},
 628             {"GGGGG"},
 629 
 630             {"y"},
 631             {"yy"},
 632             {"yyy"},
 633             {"yyyy"},
 634             {"yyyyy"},
 635 
 636             {"M"},
 637             {"MM"},
 638             {"MMM"},
 639             {"MMMM"},
 640             {"MMMMM"},
 641 
 642             {"L"},
 643             {"LL"},
 644             {"LLL"},
 645             {"LLLL"},
 646             {"LLLLL"},
 647 
 648             {"D"},
 649             {"DD"},
 650             {"DDD"},
 651 
 652             {"d"},
 653             {"dd"},
 654 
 655             {"F"},
 656 
 657             {"Q"},
 658             {"QQ"},
 659             {"QQQ"},
 660             {"QQQQ"},
 661             {"QQQQQ"},
 662 
 663             {"q"},
 664             {"qq"},
 665             {"qqq"},
 666             {"qqqq"},
 667             {"qqqqq"},
 668 
 669             {"E"},
 670             {"EE"},
 671             {"EEE"},
 672             {"EEEE"},
 673             {"EEEEE"},
 674 
 675             {"e"},
 676             {"ee"},
 677             {"eee"},
 678             {"eeee"},
 679             {"eeeee"},
 680 
 681             {"c"},
 682             {"ccc"},
 683             {"cccc"},
 684             {"ccccc"},
 685 
 686             {"a"},
 687 
 688             {"H"},
 689             {"HH"},
 690 
 691             {"K"},
 692             {"KK"},
 693 
 694             {"k"},
 695             {"kk"},
 696 
 697             {"h"},
 698             {"hh"},
 699 
 700             {"m"},
 701             {"mm"},
 702 
 703             {"s"},
 704             {"ss"},
 705 
 706             {"S"},
 707             {"SS"},
 708             {"SSS"},
 709             {"SSSSSSSSS"},
 710 
 711             {"A"},
 712             {"AA"},
 713             {"AAA"},
 714 
 715             {"n"},
 716             {"nn"},
 717             {"nnn"},
 718 
 719             {"N"},
 720             {"NN"},
 721             {"NNN"},
 722 
 723             {"z"},
 724             {"zz"},
 725             {"zzz"},
 726             {"zzzz"},
 727 
 728             {"VV"},
 729 
 730             {"Z"},
 731             {"ZZ"},
 732             {"ZZZ"},
 733 
 734             {"X"},
 735             {"XX"},
 736             {"XXX"},
 737             {"XXXX"},
 738             {"XXXXX"},
 739 
 740             {"x"},
 741             {"xx"},
 742             {"xxx"},
 743             {"xxxx"},
 744             {"xxxxx"},
 745 
 746             {"ppH"},
 747             {"pppDD"},
 748 
 749             {"yyyy[-MM[-dd"},
 750             {"yyyy[-MM[-dd]]"},
 751             {"yyyy[-MM[]-dd]"},
 752 
 753             {"yyyy-MM-dd'T'HH:mm:ss.SSS"},
 754 
 755             {"e"},
 756             {"w"},
 757             {"ww"},
 758             {"W"},
 759             {"W"},
 760 
 761             {"g"},
 762             {"ggggg"},
 763         };
 764     }
 765 
 766     @Test(dataProvider="validPatterns")
 767     public void test_appendPattern_valid(String input) throws Exception {
 768         builder.appendPattern(input);  // test is for no error here
 769     }
 770 
 771     //-----------------------------------------------------------------------
 772     @DataProvider(name="invalidPatterns")
 773     Object[][] dataInvalid() {
 774         return new Object[][] {
 775             {"'"},
 776             {"'hello"},
 777             {"'hel''lo"},
 778             {"'hello''"},
 779             {"{"},
 780             {"}"},
 781             {"{}"},
 782             {"#"},
 783             {"]"},
 784             {"yyyy]"},
 785             {"yyyy]MM"},
 786             {"yyyy[MM]]"},
 787 
 788             {"aa"},
 789             {"aaa"},
 790             {"aaaa"},
 791             {"aaaaa"},
 792             {"aaaaaa"},
 793             {"MMMMMM"},
 794             {"QQQQQQ"},
 795             {"qqqqqq"},
 796             {"EEEEEE"},
 797             {"eeeeee"},
 798             {"cc"},
 799             {"cccccc"},
 800             {"ddd"},
 801             {"DDDD"},
 802             {"FF"},
 803             {"FFF"},
 804             {"hhh"},
 805             {"HHH"},
 806             {"kkk"},
 807             {"KKK"},
 808             {"mmm"},
 809             {"sss"},
 810             {"OO"},
 811             {"OOO"},
 812             {"OOOOO"},
 813             {"XXXXXX"},
 814             {"zzzzz"},
 815             {"ZZZZZZ"},
 816 
 817             {"RO"},
 818 
 819             {"p"},
 820             {"pp"},
 821             {"p:"},
 822 
 823             {"f"},
 824             {"ff"},
 825             {"f:"},
 826             {"fy"},
 827             {"fa"},
 828             {"fM"},
 829 
 830             {"www"},
 831             {"WW"},
 832 
 833             {"vv"},
 834             {"vvv"},
 835         };
 836     }
 837 
 838     @Test(dataProvider="invalidPatterns", expectedExceptions=IllegalArgumentException.class)
 839     public void test_appendPattern_invalid(String input) throws Exception {
 840         builder.appendPattern(input);  // test is for error here
 841     }
 842 
 843     //-----------------------------------------------------------------------
 844     @DataProvider(name="patternPrint")
 845     Object[][] data_patternPrint() {
 846         return new Object[][] {
 847             {"Q", date(2012, 2, 10), "1"},
 848             {"QQ", date(2012, 2, 10), "01"},
 849             {"QQQ", date(2012, 2, 10), "Q1"},
 850             {"QQQQ", date(2012, 2, 10), "1st quarter"},
 851             {"QQQQQ", date(2012, 2, 10), "1"},
 852         };
 853     }
 854 
 855     @Test(dataProvider="patternPrint")
 856     public void test_appendPattern_patternPrint(String input, Temporal temporal, String expected) throws Exception {
 857         DateTimeFormatter f = builder.appendPattern(input).toFormatter(Locale.UK);
 858         String test = f.format(temporal);
 859         assertEquals(test, expected);
 860     }
 861 
 862     private static Temporal date(int y, int m, int d) {
 863         return LocalDate.of(y, m, d);
 864     }
 865 
 866     //-----------------------------------------------------------------------
 867     @DataProvider(name="modJulianFieldPattern")
 868     Object[][] data_modJuilanFieldPattern() {
 869         return new Object[][] {
 870             {"g", "1"},
 871             {"g", "123456"},
 872             {"gggggg", "123456"},
 873         };
 874     }
 875 
 876     @Test(dataProvider="modJulianFieldPattern")
 877     public void test_modJulianFieldPattern(String pattern, String input) throws Exception {
 878         DateTimeFormatter.ofPattern(pattern).parse(input);
 879     }
 880 
 881     @DataProvider(name="modJulianFieldValues")
 882     Object[][] data_modJuilanFieldValues() {
 883         return new Object[][] {
 884             {1970, 1, 1, "40587"},
 885             {1858, 11, 17, "0"},
 886             {1858, 11, 16, "-1"},
 887         };
 888     }
 889 
 890     @Test(dataProvider="modJulianFieldValues")
 891     public void test_modJulianFieldValues(int y, int m, int d, String expected) throws Exception {
 892         DateTimeFormatter df = new DateTimeFormatterBuilder().appendPattern("g").toFormatter();
 893          assertEquals(LocalDate.of(y, m, d).format(df), expected);
 894     }
 895     //----------------------------------------------------------------------
 896     @DataProvider(name="dayOfYearFieldValues")
 897     Object[][] data_dayOfYearFieldValues() {
 898         return new Object[][] {
 899                 {2016, 1, 1, "D", "1"},
 900                 {2016, 1, 31, "D", "31"},
 901                 {2016, 1, 1, "DD", "01"},
 902                 {2016, 1, 31, "DD", "31"},
 903                 {2016, 4, 9, "DD", "100"},
 904                 {2016, 1, 1, "DDD", "001"},
 905                 {2016, 1, 31, "DDD", "031"},
 906                 {2016, 4, 9, "DDD", "100"},
 907         };
 908     }
 909 
 910     @Test(dataProvider="dayOfYearFieldValues")
 911     public void test_dayOfYearFieldValues(int y, int m, int d, String pattern, String expected) throws Exception {
 912         DateTimeFormatter df = new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter();
 913         assertEquals(LocalDate.of(y, m, d).format(df), expected);
 914     }
 915 
 916     @DataProvider(name="dayOfYearFieldAdjacentParsingValues")
 917     Object[][] data_dayOfYearFieldAdjacentParsingValues() {
 918         return new Object[][] {
 919             {"20160281015", LocalDateTime.of(2016, 1, 28, 10, 15)},
 920             {"20161001015", LocalDateTime.of(2016, 4, 9, 10, 15)},
 921         };
 922     }
 923 
 924     @Test(dataProvider="dayOfYearFieldAdjacentParsingValues")
 925     public void test_dayOfYearFieldAdjacentValueParsing(String input, LocalDateTime expected) {
 926         DateTimeFormatter df = new DateTimeFormatterBuilder().appendPattern("yyyyDDDHHmm").toFormatter();
 927         LocalDateTime actual = LocalDateTime.parse(input, df);
 928         assertEquals(actual, expected);
 929     }
 930 
 931     @Test(expectedExceptions = DateTimeParseException.class)
 932     public void test_dayOfYearFieldInvalidValue() {
 933         DateTimeFormatter.ofPattern("DDD").parse("1234");
 934     }
 935 
 936     @Test(expectedExceptions = DateTimeParseException.class)
 937     public void test_dayOfYearFieldInvalidAdacentValueParsingPattern() {
 938         // patterns D and DD will not take part in adjacent value parsing
 939         DateTimeFormatter.ofPattern("yyyyDDHHmmss").parse("201610123456");
 940     }
 941 
 942     //-----------------------------------------------------------------------
 943     @DataProvider(name="secondsPattern")
 944     Object[][] data_secondsPattern() {
 945         return new Object[][] {
 946                 {"A", "1", LocalTime.ofNanoOfDay(1_000_000)},
 947                 {"A", "100000", LocalTime.ofSecondOfDay(100)},
 948                 {"AA", "01", LocalTime.ofNanoOfDay(1_000_000)},
 949                 {"AA", "100000", LocalTime.ofSecondOfDay(100)},
 950                 {"AAAAAA", "100000", LocalTime.ofSecondOfDay(100)},
 951                 {"HHmmssn", "0000001", LocalTime.ofNanoOfDay(1)},
 952                 {"HHmmssn", "000000111", LocalTime.ofNanoOfDay(111)},
 953                 {"HHmmssnn", "00000001", LocalTime.ofNanoOfDay(1)},
 954                 {"HHmmssnn", "0000001111", LocalTime.ofNanoOfDay(1111)},
 955                 {"HHmmssnnnnnn", "000000111111", LocalTime.ofNanoOfDay(111_111)},
 956                 {"N", "1", LocalTime.ofNanoOfDay(1)},
 957                 {"N", "100000", LocalTime.ofNanoOfDay(100_000)},
 958                 {"NN", "01", LocalTime.ofNanoOfDay(1)},
 959                 {"NN", "100000", LocalTime.ofNanoOfDay(100_000)},
 960                 {"NNNNNN", "100000", LocalTime.ofNanoOfDay(100_000)},
 961         };
 962     }
 963 
 964     @Test(dataProvider="secondsPattern")
 965     public void test_secondsPattern(String pattern, String input, LocalTime expected) throws Exception {
 966         DateTimeFormatter df = new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter();
 967         assertEquals(LocalTime.parse(input, df), expected);
 968     }
 969 
 970     @DataProvider(name="secondsValues")
 971     Object[][] data_secondsValues() {
 972         return new Object[][] {
 973                 {"A", 1, "1000"},
 974                 {"n", 1, "0"},
 975                 {"N", 1, "1000000000"},
 976         };
 977     }
 978 
 979     @Test(dataProvider="secondsValues")
 980     public void test_secondsValues(String pattern, int seconds , String expected) throws Exception {
 981         DateTimeFormatter df = new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter();
 982         assertEquals(LocalTime.ofSecondOfDay(seconds).format(df), expected);
 983     }
 984 
 985     @Test(expectedExceptions = DateTimeParseException.class)
 986     public void test_secondsPatternInvalidAdacentValueParsingPattern() {
 987         // patterns A*, N*, n* will not take part in adjacent value parsing
 988         DateTimeFormatter.ofPattern("yyyyAA").parse("201610");
 989     }
 990 
 991     //-----------------------------------------------------------------------
 992     @Test
 993     public void test_adjacent_strict_firstFixedWidth() throws Exception {
 994         // succeeds because both number elements are fixed width
 995         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK);
 996         ParsePosition pp = new ParsePosition(0);
 997         TemporalAccessor parsed = f.parseUnresolved("12309", pp);
 998         assertEquals(pp.getErrorIndex(), -1);
 999         assertEquals(pp.getIndex(), 5);
1000         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
1001         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
1002     }
1003 
1004     @Test
1005     public void test_adjacent_strict_firstVariableWidth_success() throws Exception {
1006         // succeeds greedily parsing variable width, then fixed width, to non-numeric Z
1007         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('Z').toFormatter(Locale.UK);
1008         ParsePosition pp = new ParsePosition(0);
1009         TemporalAccessor parsed = f.parseUnresolved("12309Z", pp);
1010         assertEquals(pp.getErrorIndex(), -1);
1011         assertEquals(pp.getIndex(), 6);
1012         assertEquals(parsed.getLong(HOUR_OF_DAY), 123L);
1013         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 9L);
1014     }
1015 
1016     @Test
1017     public void test_adjacent_strict_firstVariableWidth_fails() throws Exception {
1018         // fails because literal is a number and variable width parse greedily absorbs it
1019         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK);
1020         ParsePosition pp = new ParsePosition(0);
1021         TemporalAccessor parsed = f.parseUnresolved("12309", pp);
1022         assertEquals(pp.getErrorIndex(), 5);
1023         assertEquals(parsed, null);
1024     }
1025 
1026     @Test
1027     public void test_adjacent_lenient() throws Exception {
1028         // succeeds because both number elements are fixed width even in lenient mode
1029         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK);
1030         ParsePosition pp = new ParsePosition(0);
1031         TemporalAccessor parsed = f.parseUnresolved("12309", pp);
1032         assertEquals(pp.getErrorIndex(), -1);
1033         assertEquals(pp.getIndex(), 5);
1034         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
1035         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
1036     }
1037 
1038     @Test
1039     public void test_adjacent_lenient_firstVariableWidth_success() throws Exception {
1040         // succeeds greedily parsing variable width, then fixed width, to non-numeric Z
1041         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('Z').toFormatter(Locale.UK);
1042         ParsePosition pp = new ParsePosition(0);
1043         TemporalAccessor parsed = f.parseUnresolved("12309Z", pp);
1044         assertEquals(pp.getErrorIndex(), -1);
1045         assertEquals(pp.getIndex(), 6);
1046         assertEquals(parsed.getLong(HOUR_OF_DAY), 123L);
1047         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 9L);
1048     }
1049 
1050     @Test
1051     public void test_adjacent_lenient_firstVariableWidth_fails() throws Exception {
1052         // fails because literal is a number and variable width parse greedily absorbs it
1053         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK);
1054         ParsePosition pp = new ParsePosition(0);
1055         TemporalAccessor parsed = f.parseUnresolved("12309", pp);
1056         assertEquals(pp.getErrorIndex(), 5);
1057         assertEquals(parsed, null);
1058     }
1059 
1060     //-----------------------------------------------------------------------
1061     @Test
1062     public void test_adjacent_strict_fractionFollows() throws Exception {
1063         // succeeds because hour/min are fixed width
1064         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 0, 3, false).toFormatter(Locale.UK);
1065         ParsePosition pp = new ParsePosition(0);
1066         TemporalAccessor parsed = f.parseUnresolved("1230567", pp);
1067         assertEquals(pp.getErrorIndex(), -1);
1068         assertEquals(pp.getIndex(), 7);
1069         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
1070         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
1071         assertEquals(parsed.getLong(NANO_OF_SECOND), 567_000_000L);
1072     }
1073 
1074     @Test
1075     public void test_adjacent_strict_fractionFollows_2digit() throws Exception {
1076         // succeeds because hour/min are fixed width
1077         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 0, 3, false).toFormatter(Locale.UK);
1078         ParsePosition pp = new ParsePosition(0);
1079         TemporalAccessor parsed = f.parseUnresolved("123056", pp);
1080         assertEquals(pp.getErrorIndex(), -1);
1081         assertEquals(pp.getIndex(), 6);
1082         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
1083         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
1084         assertEquals(parsed.getLong(NANO_OF_SECOND), 560_000_000L);
1085     }
1086 
1087     @Test
1088     public void test_adjacent_strict_fractionFollows_0digit() throws Exception {
1089         // succeeds because hour/min are fixed width
1090         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 0, 3, false).toFormatter(Locale.UK);
1091         ParsePosition pp = new ParsePosition(0);
1092         TemporalAccessor parsed = f.parseUnresolved("1230", pp);
1093         assertEquals(pp.getErrorIndex(), -1);
1094         assertEquals(pp.getIndex(), 4);
1095         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
1096         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
1097     }
1098 
1099     @Test
1100     public void test_adjacent_lenient_fractionFollows() throws Exception {
1101         // succeeds because hour/min are fixed width
1102         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 3, 3, false).toFormatter(Locale.UK);
1103         ParsePosition pp = new ParsePosition(0);
1104         TemporalAccessor parsed = f.parseUnresolved("1230567", pp);
1105         assertEquals(pp.getErrorIndex(), -1);
1106         assertEquals(pp.getIndex(), 7);
1107         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
1108         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
1109         assertEquals(parsed.getLong(NANO_OF_SECOND), 567_000_000L);
1110     }
1111 
1112     @Test
1113     public void test_adjacent_lenient_fractionFollows_2digit() throws Exception {
1114         // succeeds because hour/min are fixed width
1115         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 3, 3, false).toFormatter(Locale.UK);
1116         ParsePosition pp = new ParsePosition(0);
1117         TemporalAccessor parsed = f.parseUnresolved("123056", pp);
1118         assertEquals(pp.getErrorIndex(), -1);
1119         assertEquals(pp.getIndex(), 6);
1120         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
1121         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
1122         assertEquals(parsed.getLong(NANO_OF_SECOND), 560_000_000L);
1123     }
1124 
1125     @Test
1126     public void test_adjacent_lenient_fractionFollows_0digit() throws Exception {
1127         // succeeds because hour, min and fraction of seconds are fixed width
1128         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 3, 3, false).toFormatter(Locale.UK);
1129         ParsePosition pp = new ParsePosition(0);
1130         TemporalAccessor parsed = f.parseUnresolved("1230", pp);
1131         assertEquals(pp.getErrorIndex(), -1);
1132         assertEquals(pp.getIndex(), 4);
1133         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
1134         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
1135     }
1136 
1137     @DataProvider(name="adjacentFractionParseData")
1138     Object[][] data_adjacent_fraction_parse() {
1139         return new Object[][] {
1140             {"20130812214600025", "yyyyMMddHHmmssSSS", LocalDateTime.of(2013, 8, 12, 21, 46, 00, 25000000)},
1141             {"201308122146000256", "yyyyMMddHHmmssSSSS", LocalDateTime.of(2013, 8, 12, 21, 46, 00, 25600000)},
1142         };
1143     }
1144 
1145     @Test(dataProvider = "adjacentFractionParseData")
1146     public void test_adjacent_fraction(String input, String pattern, LocalDateTime expected) {
1147         DateTimeFormatter dtf = DateTimeFormatter.ofPattern(pattern);
1148         LocalDateTime actual = LocalDateTime.parse(input, dtf);
1149         assertEquals(actual, expected);
1150     }
1151 
1152     @DataProvider(name="lenientOffsetParseData")
1153     Object[][] data_lenient_offset_parse() {
1154         return new Object[][] {
1155             {"+HH", "+01", 3600},
1156             {"+HH", "+0101", 3660},
1157             {"+HH", "+010101", 3661},
1158             {"+HH", "+01", 3600},
1159             {"+HH", "+01:01", 3660},
1160             {"+HH", "+01:01:01", 3661},
1161             {"+HHmm", "+01", 3600},
1162             {"+HHmm", "+0101", 3660},
1163             {"+HHmm", "+010101", 3661},
1164             {"+HH:mm", "+01", 3600},
1165             {"+HH:mm", "+01:01", 3660},
1166             {"+HH:mm", "+01:01:01", 3661},
1167             {"+HHMM", "+01", 3600},
1168             {"+HHMM", "+0101", 3660},
1169             {"+HHMM", "+010101", 3661},
1170             {"+HH:MM", "+01", 3600},
1171             {"+HH:MM", "+01:01", 3660},
1172             {"+HH:MM", "+01:01:01", 3661},
1173             {"+HHMMss", "+01", 3600},
1174             {"+HHMMss", "+0101", 3660},
1175             {"+HHMMss", "+010101", 3661},
1176             {"+HH:MM:ss", "+01", 3600},
1177             {"+HH:MM:ss", "+01:01", 3660},
1178             {"+HH:MM:ss", "+01:01:01", 3661},
1179             {"+HHMMSS", "+01", 3600},
1180             {"+HHMMSS", "+0101", 3660},
1181             {"+HHMMSS", "+010101", 3661},
1182             {"+HH:MM:SS", "+01", 3600},
1183             {"+HH:MM:SS", "+01:01", 3660},
1184             {"+HH:MM:SS", "+01:01:01", 3661},
1185             {"+HHmmss", "+01", 3600},
1186             {"+HHmmss", "+0101", 3660},
1187             {"+HHmmss", "+010101", 3661},
1188             {"+HH:mm:ss", "+01", 3600},
1189             {"+HH:mm:ss", "+01:01", 3660},
1190             {"+HH:mm:ss", "+01:01:01", 3661},
1191         };
1192     }
1193 
1194     @Test(dataProvider="lenientOffsetParseData")
1195     public void test_lenient_offset_parse_1(String pattern, String offset, int offsetSeconds) {
1196         assertEquals(new DateTimeFormatterBuilder().parseLenient().appendOffset(pattern, "Z").toFormatter().parse(offset).get(OFFSET_SECONDS),
1197                      offsetSeconds);
1198     }
1199 
1200     @Test
1201     public void test_lenient_offset_parse_2() {
1202         assertEquals(new DateTimeFormatterBuilder().parseLenient().appendOffsetId().toFormatter().parse("+01").get(OFFSET_SECONDS),
1203                      3600);
1204     }
1205 
1206     @Test(expectedExceptions=DateTimeParseException.class)
1207     public void test_strict_appendOffsetId() {
1208         assertEquals(new DateTimeFormatterBuilder().appendOffsetId().toFormatter().parse("+01").get(OFFSET_SECONDS),
1209                      3600);
1210     }
1211 
1212     @Test(expectedExceptions=DateTimeParseException.class)
1213     public void test_strict_appendOffset_1() {
1214         assertEquals(new DateTimeFormatterBuilder().appendOffset("+HH:MM:ss", "Z").toFormatter().parse("+01").get(OFFSET_SECONDS),
1215                      3600);
1216     }
1217 
1218     @Test(expectedExceptions=DateTimeParseException.class)
1219     public void test_strict_appendOffset_2() {
1220         assertEquals(new DateTimeFormatterBuilder().appendOffset("+HHMMss", "Z").toFormatter().parse("+01").get(OFFSET_SECONDS),
1221                      3600);
1222     }
1223 
1224     @Test
1225     public void test_basic_iso_date() {
1226         assertEquals(BASIC_ISO_DATE.parse("20021231+01").get(OFFSET_SECONDS), 3600);
1227         assertEquals(BASIC_ISO_DATE.parse("20021231+0101").get(OFFSET_SECONDS), 3660);
1228     }
1229 
1230 }