1 /*
   2  * Copyright (c) 2012, 2019, 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                 {"+H", 2, 0, 0, "+2"},
 411                 {"+H", -2, 0, 0, "-2"},
 412                 {"+H", 2, 30, 0, "+2"},
 413                 {"+H", 2, 0, 45, "+2"},
 414                 {"+H", 2, 30, 45, "+2"},
 415                 {"+H", 12, 0, 0, "+12"},
 416                 {"+H", -12, 0, 0, "-12"},
 417                 {"+H", 12, 30, 0, "+12"},
 418                 {"+H", 12, 0, 45, "+12"},
 419                 {"+H", 12, 30, 45, "+12"},
 420 
 421                 {"+Hmm", 2, 0, 0, "+2"},
 422                 {"+Hmm", -2, 0, 0, "-2"},
 423                 {"+Hmm", 2, 30, 0, "+230"},
 424                 {"+Hmm", 2, 0, 45, "+2"},
 425                 {"+Hmm", 2, 30, 45, "+230"},
 426                 {"+Hmm", 12, 0, 0, "+12"},
 427                 {"+Hmm", -12, 0, 0, "-12"},
 428                 {"+Hmm", 12, 30, 0, "+1230"},
 429                 {"+Hmm", 12, 0, 45, "+12"},
 430                 {"+Hmm", 12, 30, 45, "+1230"},
 431 
 432                 {"+H:mm", 2, 0, 0, "+2"},
 433                 {"+H:mm", -2, 0, 0, "-2"},
 434                 {"+H:mm", 2, 30, 0, "+2:30"},
 435                 {"+H:mm", 2, 0, 45, "+2"},
 436                 {"+H:mm", 2, 30, 45, "+2:30"},
 437                 {"+H:mm", 12, 0, 0, "+12"},
 438                 {"+H:mm", -12, 0, 0, "-12"},
 439                 {"+H:mm", 12, 30, 0, "+12:30"},
 440                 {"+H:mm", 12, 0, 45, "+12"},
 441                 {"+H:mm", 12, 30, 45, "+12:30"},
 442 
 443                 {"+HMM", 2, 0, 0, "+200"},
 444                 {"+HMM", -2, 0, 0, "-200"},
 445                 {"+HMM", 2, 30, 0, "+230"},
 446                 {"+HMM", 2, 0, 45, "+200"},
 447                 {"+HMM", 2, 30, 45, "+230"},
 448                 {"+HMM", 12, 0, 0, "+1200"},
 449                 {"+HMM", -12, 0, 0, "-1200"},
 450                 {"+HMM", 12, 30, 0, "+1230"},
 451                 {"+HMM", 12, 0, 45, "+1200"},
 452                 {"+HMM", 12, 30, 45, "+1230"},
 453 
 454                 {"+H:MM", 2, 0, 0, "+2:00"},
 455                 {"+H:MM", -2, 0, 0, "-2:00"},
 456                 {"+H:MM", 2, 30, 0, "+2:30"},
 457                 {"+H:MM", 2, 0, 45, "+2:00"},
 458                 {"+H:MM", 2, 30, 45, "+2:30"},
 459                 {"+H:MM", 12, 0, 0, "+12:00"},
 460                 {"+H:MM", -12, 0, 0, "-12:00"},
 461                 {"+H:MM", 12, 30, 0, "+12:30"},
 462                 {"+H:MM", 12, 0, 45, "+12:00"},
 463                 {"+H:MM", 12, 30, 45, "+12:30"},
 464 
 465                 {"+HMMss", 2, 0, 0, "+200"},
 466                 {"+HMMss", -2, 0, 0, "-200"},
 467                 {"+HMMss", 2, 30, 0, "+230"},
 468                 {"+HMMss", 2, 0, 45, "+20045"},
 469                 {"+HMMss", 2, 30, 45, "+23045"},
 470                 {"+HMMss", 12, 0, 0, "+1200"},
 471                 {"+HMMss", -12, 0, 0, "-1200"},
 472                 {"+HMMss", 12, 30, 0, "+1230"},
 473                 {"+HMMss", 12, 0, 45, "+120045"},
 474                 {"+HMMss", 12, 30, 45, "+123045"},
 475 
 476                 {"+H:MM:ss", 2, 0, 0, "+2:00"},
 477                 {"+H:MM:ss", -2, 0, 0, "-2:00"},
 478                 {"+H:MM:ss", 2, 30, 0, "+2:30"},
 479                 {"+H:MM:ss", 2, 0, 45, "+2:00:45"},
 480                 {"+H:MM:ss", 2, 30, 45, "+2:30:45"},
 481                 {"+H:MM:ss", 12, 0, 0, "+12:00"},
 482                 {"+H:MM:ss", -12, 0, 0, "-12:00"},
 483                 {"+H:MM:ss", 12, 30, 0, "+12:30"},
 484                 {"+H:MM:ss", 12, 0, 45, "+12:00:45"},
 485                 {"+H:MM:ss", 12, 30, 45, "+12:30:45"},
 486 
 487                 {"+HMMSS", 2, 0, 0, "+20000"},
 488                 {"+HMMSS", -2, 0, 0, "-20000"},
 489                 {"+HMMSS", 2, 30, 0, "+23000"},
 490                 {"+HMMSS", 2, 0, 45, "+20045"},
 491                 {"+HMMSS", 2, 30, 45, "+23045"},
 492                 {"+HMMSS", 12, 0, 0, "+120000"},
 493                 {"+HMMSS", -12, 0, 0, "-120000"},
 494                 {"+HMMSS", 12, 30, 0, "+123000"},
 495                 {"+HMMSS", 12, 0, 45, "+120045"},
 496                 {"+HMMSS", 12, 30, 45, "+123045"},
 497 
 498                 {"+H:MM:SS", 2, 0, 0, "+2:00:00"},
 499                 {"+H:MM:SS", -2, 0, 0, "-2:00:00"},
 500                 {"+H:MM:SS", 2, 30, 0, "+2:30:00"},
 501                 {"+H:MM:SS", 2, 0, 45, "+2:00:45"},
 502                 {"+H:MM:SS", 2, 30, 45, "+2:30:45"},
 503                 {"+H:MM:SS", 12, 0, 0, "+12:00:00"},
 504                 {"+H:MM:SS", -12, 0, 0, "-12:00:00"},
 505                 {"+H:MM:SS", 12, 30, 0, "+12:30:00"},
 506                 {"+H:MM:SS", 12, 0, 45, "+12:00:45"},
 507                 {"+H:MM:SS", 12, 30, 45, "+12:30:45"},
 508 
 509                 {"+Hmmss", 2, 0, 0, "+2"},
 510                 {"+Hmmss", -2, 0, 0, "-2"},
 511                 {"+Hmmss", 2, 30, 0, "+230"},
 512                 {"+Hmmss", 2, 0, 45, "+20045"},
 513                 {"+Hmmss", 2, 30, 45, "+23045"},
 514                 {"+Hmmss", 12, 0, 0, "+12"},
 515                 {"+Hmmss", -12, 0, 0, "-12"},
 516                 {"+Hmmss", 12, 30, 0, "+1230"},
 517                 {"+Hmmss", 12, 0, 45, "+120045"},
 518                 {"+Hmmss", 12, 30, 45, "+123045"},
 519 
 520                 {"+H:mm:ss", 2, 0, 0, "+2"},
 521                 {"+H:mm:ss", -2, 0, 0, "-2"},
 522                 {"+H:mm:ss", 2, 30, 0, "+2:30"},
 523                 {"+H:mm:ss", 2, 0, 45, "+2:00:45"},
 524                 {"+H:mm:ss", 2, 30, 45, "+2:30:45"},
 525                 {"+H:mm:ss", 12, 0, 0, "+12"},
 526                 {"+H:mm:ss", -12, 0, 0, "-12"},
 527                 {"+H:mm:ss", 12, 30, 0, "+12:30"},
 528                 {"+H:mm:ss", 12, 0, 45, "+12:00:45"},
 529                 {"+H:mm:ss", 12, 30, 45, "+12:30:45"},
 530 
 531 
 532         };
 533     }
 534 
 535     @Test(dataProvider="offsetPatterns")
 536     public void test_appendOffset_format(String pattern, int h, int m, int s, String expected) throws Exception {
 537         builder.appendOffset(pattern, "Z");
 538         DateTimeFormatter f = builder.toFormatter();
 539         ZoneOffset offset = ZoneOffset.ofHoursMinutesSeconds(h, m, s);
 540         assertEquals(f.format(offset), expected);
 541     }
 542 
 543     @Test(dataProvider="offsetPatterns")
 544     public void test_appendOffset_parse(String pattern, int h, int m, int s, String expected) throws Exception {
 545         builder.appendOffset(pattern, "Z");
 546         DateTimeFormatter f = builder.toFormatter();
 547         ZoneOffset parsed = f.parse(expected, ZoneOffset::from);
 548         assertEquals(f.format(parsed), expected);
 549     }
 550 
 551     @DataProvider(name="badOffsetPatterns")
 552     Object[][] data_badOffsetPatterns() {
 553         return new Object[][] {
 554             {"HH"},
 555             {"HHMM"},
 556             {"HH:MM"},
 557             {"HHMMss"},
 558             {"HH:MM:ss"},
 559             {"HHMMSS"},
 560             {"HH:MM:SS"},
 561             {"+HHM"},
 562             {"+A"},
 563         };
 564     }
 565 
 566     @Test(dataProvider="badOffsetPatterns", expectedExceptions=IllegalArgumentException.class)
 567     public void test_appendOffset_badPattern(String pattern) throws Exception {
 568         builder.appendOffset(pattern, "Z");
 569     }
 570 
 571     @Test(expectedExceptions=NullPointerException.class)
 572     public void test_appendOffset_3arg_nullText() throws Exception {
 573         builder.appendOffset("+HH:MM", null);
 574     }
 575 
 576     @Test(expectedExceptions=NullPointerException.class)
 577     public void test_appendOffset_3arg_nullPattern() throws Exception {
 578         builder.appendOffset(null, "Z");
 579     }
 580 
 581     //-----------------------------------------------------------------------
 582     //-----------------------------------------------------------------------
 583     //-----------------------------------------------------------------------
 584     @DataProvider(name = "formatGenericTimeZonePatterns")
 585     Object[][] data_formatGenericNonLocationPatterns() {
 586         return new Object[][] {
 587                 {"v", "America/Los_Angeles", "PT"},
 588                 {"vvvv", "America/Los_Angeles", "Pacific Time"},
 589                 {"v", "America/New_York", "ET"},
 590                 {"vvvv", "America/New_York", "Eastern Time"},
 591         };
 592     }
 593 
 594     @Test(dataProvider = "formatGenericTimeZonePatterns")
 595     public void test_appendZoneText_formatGenericTimeZonePatterns(String pattern, String input, String expected) {
 596         ZonedDateTime zdt = ZonedDateTime.of(LocalDateTime.now(), ZoneId.of(input));
 597         DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern, Locale.US);
 598         assertEquals(zdt.format(df), expected);
 599     }
 600 
 601     @DataProvider(name = "parseGenericTimeZonePatterns")
 602     Object[][]  data_parseGenericTimeZonePatterns() {
 603         return new Object[][] {
 604                 {"yyyy DDD HH mm v", LocalDateTime.of(2015, Month.MARCH, 10, 12, 13), ZoneId.of("America/Los_Angeles"),
 605                  "2015 069 12 13 PT"},
 606                 {"yyyy DDD HH mm vvvv", LocalDateTime.of(2015, Month.MARCH, 10, 12, 13), ZoneId.of("America/Los_Angeles"),
 607                  "2015 069 12 13 Pacific Time"},
 608                 {"yyyy DDD HH mm v", LocalDateTime.of(2015, Month.NOVEMBER, 10, 12, 13), ZoneId.of("America/Los_Angeles"),
 609                  "2015 314 12 13 PT"},
 610                 {"yyyy DDD HH mm vvvv", LocalDateTime.of(2015, Month.NOVEMBER, 10, 12, 13), ZoneId.of("America/Los_Angeles"),
 611                  "2015 314 12 13 Pacific Time"},
 612         };
 613     }
 614 
 615     @Test(dataProvider = "parseGenericTimeZonePatterns")
 616     public void test_appendZoneText_parseGenericTimeZonePatterns(String pattern, LocalDateTime ldt, ZoneId zId, String input) {
 617         DateTimeFormatter df = new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter(Locale.US);
 618         ZonedDateTime expected = ZonedDateTime.parse(input, df);
 619         ZonedDateTime actual = ZonedDateTime.of(ldt, zId);
 620         assertEquals(actual, expected);
 621     }
 622 
 623     @DataProvider(name = "formatNonGenericTimeZonePatterns_1")
 624     Object[][]  data_formatNonGenericTimeZonePatterns_1() {
 625         return new Object[][] {
 626                 {"yyyy-MM-dd HH:mm:ss z", LocalDateTime.of(2015, Month.NOVEMBER, 1, 0, 30),
 627                  "2015-11-01 00:30:00 PDT"},
 628                 {"yyyy-MM-dd HH:mm:ss z", LocalDateTime.of(2015, Month.NOVEMBER, 1, 1, 30),
 629                  "2015-11-01 01:30:00 PDT"},
 630                 {"yyyy-MM-dd HH:mm:ss z", LocalDateTime.of(2015, Month.NOVEMBER, 1, 2, 30),
 631                  "2015-11-01 02:30:00 PST"},
 632                 {"yyyy-MM-dd HH:mm:ss zzzz", LocalDateTime.of(2015, Month.NOVEMBER, 1, 0, 30),
 633                  "2015-11-01 00:30:00 Pacific Daylight Time"},
 634                 {"yyyy-MM-dd HH:mm:ss zzzz", LocalDateTime.of(2015, Month.NOVEMBER, 1, 1, 30),
 635                  "2015-11-01 01:30:00 Pacific Daylight Time"},
 636                 {"yyyy-MM-dd HH:mm:ss zzzz", LocalDateTime.of(2015, Month.NOVEMBER, 1, 2, 30),
 637                  "2015-11-01 02:30:00 Pacific Standard Time"},
 638         };
 639     }
 640 
 641     @Test(dataProvider = "formatNonGenericTimeZonePatterns_1")
 642     public void test_appendZoneText_parseNonGenricTimeZonePatterns_1(String pattern, LocalDateTime ldt, String expected) {
 643         ZoneId  zId = ZoneId.of("America/Los_Angeles");
 644         DateTimeFormatter df = new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter(Locale.US);
 645         ZonedDateTime zdt = ZonedDateTime.of(ldt, zId);
 646         String actual = df.format(zdt);
 647         assertEquals(actual, expected);
 648     }
 649 
 650     @DataProvider(name = "formatNonGenericTimeZonePatterns_2")
 651     Object[][]  data_formatNonGenericTimeZonePatterns_2() {
 652         return new Object[][] {
 653                 {"yyyy-MM-dd HH:mm:ss z", LocalDateTime.of(2015, Month.NOVEMBER, 1, 0, 30),
 654                  "2015-11-01 00:30:00 PDT"},
 655                 {"yyyy-MM-dd HH:mm:ss z", LocalDateTime.of(2015, Month.NOVEMBER, 1, 1, 30),
 656                  "2015-11-01 01:30:00 PT"},
 657                 {"yyyy-MM-dd HH:mm:ss z", LocalDateTime.of(2015, Month.NOVEMBER, 1, 2, 30),
 658                  "2015-11-01 02:30:00 PST"},
 659                 {"yyyy-MM-dd HH:mm:ss zzzz", LocalDateTime.of(2015, Month.NOVEMBER, 1, 0, 30),
 660                  "2015-11-01 00:30:00 Pacific Daylight Time"},
 661                 {"yyyy-MM-dd HH:mm:ss zzzz", LocalDateTime.of(2015, Month.NOVEMBER, 1, 1, 30),
 662                  "2015-11-01 01:30:00 Pacific Time"},
 663                 {"yyyy-MM-dd HH:mm:ss zzzz", LocalDateTime.of(2015, Month.NOVEMBER, 1, 2, 30),
 664                  "2015-11-01 02:30:00 Pacific Standard Time"},
 665         };
 666     }
 667 
 668     @Test(dataProvider = "formatNonGenericTimeZonePatterns_2")
 669     public void test_appendZoneText_parseNonGenricTimeZonePatterns_2(String pattern, LocalDateTime ldt, String expected) {
 670         ZoneId  zId = ZoneId.of("America/Los_Angeles");
 671         DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern, Locale.US).withZone(zId);
 672         String actual = df.format(ldt);
 673         assertEquals(actual, expected);
 674     }
 675 
 676     @Test(expectedExceptions=NullPointerException.class)
 677     public void test_appendZoneText_1arg_nullText() throws Exception {
 678         builder.appendZoneText(null);
 679     }
 680 
 681     //-----------------------------------------------------------------------
 682     //-----------------------------------------------------------------------
 683     //-----------------------------------------------------------------------
 684     @Test
 685     public void test_padNext_1arg() {
 686         builder.appendValue(MONTH_OF_YEAR).appendLiteral(':').padNext(2).appendValue(DAY_OF_MONTH);
 687         assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2: 1");
 688     }
 689 
 690     @Test(expectedExceptions=IllegalArgumentException.class)
 691     public void test_padNext_1arg_invalidWidth() throws Exception {
 692         builder.padNext(0);
 693     }
 694 
 695     //-----------------------------------------------------------------------
 696     @Test
 697     public void test_padNext_2arg_dash() throws Exception {
 698         builder.appendValue(MONTH_OF_YEAR).appendLiteral(':').padNext(2, '-').appendValue(DAY_OF_MONTH);
 699         assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2:-1");
 700     }
 701 
 702     @Test(expectedExceptions=IllegalArgumentException.class)
 703     public void test_padNext_2arg_invalidWidth() throws Exception {
 704         builder.padNext(0, '-');
 705     }
 706 
 707     //-----------------------------------------------------------------------
 708     @Test
 709     public void test_padOptional() throws Exception {
 710         builder.appendValue(MONTH_OF_YEAR).appendLiteral(':')
 711                 .padNext(5).optionalStart().appendValue(DAY_OF_MONTH).optionalEnd()
 712                 .appendLiteral(':').appendValue(YEAR);
 713         assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2:    1:2013");
 714         assertEquals(builder.toFormatter().format(YearMonth.of(2013, 2)), "2:     :2013");
 715     }
 716 
 717     //-----------------------------------------------------------------------
 718     //-----------------------------------------------------------------------
 719     //-----------------------------------------------------------------------
 720     @Test(expectedExceptions=IllegalStateException.class)
 721     public void test_optionalEnd_noStart() throws Exception {
 722         builder.optionalEnd();
 723     }
 724 
 725     //-----------------------------------------------------------------------
 726     //-----------------------------------------------------------------------
 727     //-----------------------------------------------------------------------
 728     @DataProvider(name="validPatterns")
 729     Object[][] dataValid() {
 730         return new Object[][] {
 731             {"'a'"},
 732             {"''"},
 733             {"'!'"},
 734             {"!"},
 735             {"'#'"},
 736 
 737             {"'hello_people,][)('"},
 738             {"'hi'"},
 739             {"'yyyy'"},
 740             {"''''"},
 741             {"'o''clock'"},
 742 
 743             {"G"},
 744             {"GG"},
 745             {"GGG"},
 746             {"GGGG"},
 747             {"GGGGG"},
 748 
 749             {"y"},
 750             {"yy"},
 751             {"yyy"},
 752             {"yyyy"},
 753             {"yyyyy"},
 754 
 755             {"M"},
 756             {"MM"},
 757             {"MMM"},
 758             {"MMMM"},
 759             {"MMMMM"},
 760 
 761             {"L"},
 762             {"LL"},
 763             {"LLL"},
 764             {"LLLL"},
 765             {"LLLLL"},
 766 
 767             {"D"},
 768             {"DD"},
 769             {"DDD"},
 770 
 771             {"d"},
 772             {"dd"},
 773 
 774             {"F"},
 775 
 776             {"Q"},
 777             {"QQ"},
 778             {"QQQ"},
 779             {"QQQQ"},
 780             {"QQQQQ"},
 781 
 782             {"q"},
 783             {"qq"},
 784             {"qqq"},
 785             {"qqqq"},
 786             {"qqqqq"},
 787 
 788             {"E"},
 789             {"EE"},
 790             {"EEE"},
 791             {"EEEE"},
 792             {"EEEEE"},
 793 
 794             {"e"},
 795             {"ee"},
 796             {"eee"},
 797             {"eeee"},
 798             {"eeeee"},
 799 
 800             {"c"},
 801             {"ccc"},
 802             {"cccc"},
 803             {"ccccc"},
 804 
 805             {"a"},
 806 
 807             {"H"},
 808             {"HH"},
 809 
 810             {"K"},
 811             {"KK"},
 812 
 813             {"k"},
 814             {"kk"},
 815 
 816             {"h"},
 817             {"hh"},
 818 
 819             {"m"},
 820             {"mm"},
 821 
 822             {"s"},
 823             {"ss"},
 824 
 825             {"S"},
 826             {"SS"},
 827             {"SSS"},
 828             {"SSSSSSSSS"},
 829 
 830             {"A"},
 831             {"AA"},
 832             {"AAA"},
 833 
 834             {"n"},
 835             {"nn"},
 836             {"nnn"},
 837 
 838             {"N"},
 839             {"NN"},
 840             {"NNN"},
 841 
 842             {"z"},
 843             {"zz"},
 844             {"zzz"},
 845             {"zzzz"},
 846 
 847             {"VV"},
 848 
 849             {"Z"},
 850             {"ZZ"},
 851             {"ZZZ"},
 852 
 853             {"X"},
 854             {"XX"},
 855             {"XXX"},
 856             {"XXXX"},
 857             {"XXXXX"},
 858 
 859             {"x"},
 860             {"xx"},
 861             {"xxx"},
 862             {"xxxx"},
 863             {"xxxxx"},
 864 
 865             {"ppH"},
 866             {"pppDD"},
 867 
 868             {"yyyy[-MM[-dd"},
 869             {"yyyy[-MM[-dd]]"},
 870             {"yyyy[-MM[]-dd]"},
 871 
 872             {"yyyy-MM-dd'T'HH:mm:ss.SSS"},
 873 
 874             {"e"},
 875             {"w"},
 876             {"ww"},
 877             {"W"},
 878             {"W"},
 879 
 880             {"g"},
 881             {"ggggg"},
 882         };
 883     }
 884 
 885     @Test(dataProvider="validPatterns")
 886     public void test_appendPattern_valid(String input) throws Exception {
 887         builder.appendPattern(input);  // test is for no error here
 888     }
 889 
 890     //-----------------------------------------------------------------------
 891     @DataProvider(name="invalidPatterns")
 892     Object[][] dataInvalid() {
 893         return new Object[][] {
 894             {"'"},
 895             {"'hello"},
 896             {"'hel''lo"},
 897             {"'hello''"},
 898             {"{"},
 899             {"}"},
 900             {"{}"},
 901             {"#"},
 902             {"]"},
 903             {"yyyy]"},
 904             {"yyyy]MM"},
 905             {"yyyy[MM]]"},
 906 
 907             {"aa"},
 908             {"aaa"},
 909             {"aaaa"},
 910             {"aaaaa"},
 911             {"aaaaaa"},
 912             {"MMMMMM"},
 913             {"QQQQQQ"},
 914             {"qqqqqq"},
 915             {"EEEEEE"},
 916             {"eeeeee"},
 917             {"cc"},
 918             {"cccccc"},
 919             {"ddd"},
 920             {"DDDD"},
 921             {"FF"},
 922             {"FFF"},
 923             {"hhh"},
 924             {"HHH"},
 925             {"kkk"},
 926             {"KKK"},
 927             {"mmm"},
 928             {"sss"},
 929             {"OO"},
 930             {"OOO"},
 931             {"OOOOO"},
 932             {"XXXXXX"},
 933             {"zzzzz"},
 934             {"ZZZZZZ"},
 935 
 936             {"RO"},
 937 
 938             {"p"},
 939             {"pp"},
 940             {"p:"},
 941 
 942             {"f"},
 943             {"ff"},
 944             {"f:"},
 945             {"fy"},
 946             {"fa"},
 947             {"fM"},
 948 
 949             {"www"},
 950             {"WW"},
 951 
 952             {"vv"},
 953             {"vvv"},
 954         };
 955     }
 956 
 957     @Test(dataProvider="invalidPatterns", expectedExceptions=IllegalArgumentException.class)
 958     public void test_appendPattern_invalid(String input) throws Exception {
 959         builder.appendPattern(input);  // test is for error here
 960     }
 961 
 962     //-----------------------------------------------------------------------
 963     @DataProvider(name="patternPrint")
 964     Object[][] data_patternPrint() {
 965         return new Object[][] {
 966             {"Q", date(2012, 2, 10), "1"},
 967             {"QQ", date(2012, 2, 10), "01"},
 968             {"QQQ", date(2012, 2, 10), "Q1"},
 969             {"QQQQ", date(2012, 2, 10), "1st quarter"},
 970             {"QQQQQ", date(2012, 2, 10), "1"},
 971         };
 972     }
 973 
 974     @Test(dataProvider="patternPrint")
 975     public void test_appendPattern_patternPrint(String input, Temporal temporal, String expected) throws Exception {
 976         DateTimeFormatter f = builder.appendPattern(input).toFormatter(Locale.UK);
 977         String test = f.format(temporal);
 978         assertEquals(test, expected);
 979     }
 980 
 981     private static Temporal date(int y, int m, int d) {
 982         return LocalDate.of(y, m, d);
 983     }
 984 
 985     //-----------------------------------------------------------------------
 986     @DataProvider(name="modJulianFieldPattern")
 987     Object[][] data_modJuilanFieldPattern() {
 988         return new Object[][] {
 989             {"g", "1"},
 990             {"g", "123456"},
 991             {"gggggg", "123456"},
 992         };
 993     }
 994 
 995     @Test(dataProvider="modJulianFieldPattern")
 996     public void test_modJulianFieldPattern(String pattern, String input) throws Exception {
 997         DateTimeFormatter.ofPattern(pattern).parse(input);
 998     }
 999 
1000     @DataProvider(name="modJulianFieldValues")
1001     Object[][] data_modJuilanFieldValues() {
1002         return new Object[][] {
1003             {1970, 1, 1, "40587"},
1004             {1858, 11, 17, "0"},
1005             {1858, 11, 16, "-1"},
1006         };
1007     }
1008 
1009     @Test(dataProvider="modJulianFieldValues")
1010     public void test_modJulianFieldValues(int y, int m, int d, String expected) throws Exception {
1011         DateTimeFormatter df = new DateTimeFormatterBuilder().appendPattern("g").toFormatter();
1012          assertEquals(LocalDate.of(y, m, d).format(df), expected);
1013     }
1014     //----------------------------------------------------------------------
1015     @DataProvider(name="dayOfYearFieldValues")
1016     Object[][] data_dayOfYearFieldValues() {
1017         return new Object[][] {
1018                 {2016, 1, 1, "D", "1"},
1019                 {2016, 1, 31, "D", "31"},
1020                 {2016, 1, 1, "DD", "01"},
1021                 {2016, 1, 31, "DD", "31"},
1022                 {2016, 4, 9, "DD", "100"},
1023                 {2016, 1, 1, "DDD", "001"},
1024                 {2016, 1, 31, "DDD", "031"},
1025                 {2016, 4, 9, "DDD", "100"},
1026         };
1027     }
1028 
1029     @Test(dataProvider="dayOfYearFieldValues")
1030     public void test_dayOfYearFieldValues(int y, int m, int d, String pattern, String expected) throws Exception {
1031         DateTimeFormatter df = new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter();
1032         assertEquals(LocalDate.of(y, m, d).format(df), expected);
1033     }
1034 
1035     @DataProvider(name="dayOfYearFieldAdjacentParsingValues")
1036     Object[][] data_dayOfYearFieldAdjacentParsingValues() {
1037         return new Object[][] {
1038             {"20160281015", LocalDateTime.of(2016, 1, 28, 10, 15)},
1039             {"20161001015", LocalDateTime.of(2016, 4, 9, 10, 15)},
1040         };
1041     }
1042 
1043     @Test(dataProvider="dayOfYearFieldAdjacentParsingValues")
1044     public void test_dayOfYearFieldAdjacentValueParsing(String input, LocalDateTime expected) {
1045         DateTimeFormatter df = new DateTimeFormatterBuilder().appendPattern("yyyyDDDHHmm").toFormatter();
1046         LocalDateTime actual = LocalDateTime.parse(input, df);
1047         assertEquals(actual, expected);
1048     }
1049 
1050     @Test(expectedExceptions = DateTimeParseException.class)
1051     public void test_dayOfYearFieldInvalidValue() {
1052         DateTimeFormatter.ofPattern("DDD").parse("1234");
1053     }
1054 
1055     @Test(expectedExceptions = DateTimeParseException.class)
1056     public void test_dayOfYearFieldInvalidAdacentValueParsingPattern() {
1057         // patterns D and DD will not take part in adjacent value parsing
1058         DateTimeFormatter.ofPattern("yyyyDDHHmmss").parse("201610123456");
1059     }
1060 
1061     //-----------------------------------------------------------------------
1062     @DataProvider(name="secondsPattern")
1063     Object[][] data_secondsPattern() {
1064         return new Object[][] {
1065                 {"A", "1", LocalTime.ofNanoOfDay(1_000_000)},
1066                 {"A", "100000", LocalTime.ofSecondOfDay(100)},
1067                 {"AA", "01", LocalTime.ofNanoOfDay(1_000_000)},
1068                 {"AA", "100000", LocalTime.ofSecondOfDay(100)},
1069                 {"AAAAAA", "100000", LocalTime.ofSecondOfDay(100)},
1070                 {"HHmmssn", "0000001", LocalTime.ofNanoOfDay(1)},
1071                 {"HHmmssn", "000000111", LocalTime.ofNanoOfDay(111)},
1072                 {"HHmmssnn", "00000001", LocalTime.ofNanoOfDay(1)},
1073                 {"HHmmssnn", "0000001111", LocalTime.ofNanoOfDay(1111)},
1074                 {"HHmmssnnnnnn", "000000111111", LocalTime.ofNanoOfDay(111_111)},
1075                 {"N", "1", LocalTime.ofNanoOfDay(1)},
1076                 {"N", "100000", LocalTime.ofNanoOfDay(100_000)},
1077                 {"NN", "01", LocalTime.ofNanoOfDay(1)},
1078                 {"NN", "100000", LocalTime.ofNanoOfDay(100_000)},
1079                 {"NNNNNN", "100000", LocalTime.ofNanoOfDay(100_000)},
1080         };
1081     }
1082 
1083     @Test(dataProvider="secondsPattern")
1084     public void test_secondsPattern(String pattern, String input, LocalTime expected) throws Exception {
1085         DateTimeFormatter df = new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter();
1086         assertEquals(LocalTime.parse(input, df), expected);
1087     }
1088 
1089     @DataProvider(name="secondsValues")
1090     Object[][] data_secondsValues() {
1091         return new Object[][] {
1092                 {"A", 1, "1000"},
1093                 {"n", 1, "0"},
1094                 {"N", 1, "1000000000"},
1095         };
1096     }
1097 
1098     @Test(dataProvider="secondsValues")
1099     public void test_secondsValues(String pattern, int seconds , String expected) throws Exception {
1100         DateTimeFormatter df = new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter();
1101         assertEquals(LocalTime.ofSecondOfDay(seconds).format(df), expected);
1102     }
1103 
1104     @Test(expectedExceptions = DateTimeParseException.class)
1105     public void test_secondsPatternInvalidAdacentValueParsingPattern() {
1106         // patterns A*, N*, n* will not take part in adjacent value parsing
1107         DateTimeFormatter.ofPattern("yyyyAA").parse("201610");
1108     }
1109 
1110     //-----------------------------------------------------------------------
1111     @Test
1112     public void test_adjacent_strict_firstFixedWidth() throws Exception {
1113         // succeeds because both number elements are fixed width
1114         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK);
1115         ParsePosition pp = new ParsePosition(0);
1116         TemporalAccessor parsed = f.parseUnresolved("12309", pp);
1117         assertEquals(pp.getErrorIndex(), -1);
1118         assertEquals(pp.getIndex(), 5);
1119         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
1120         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
1121     }
1122 
1123     @Test
1124     public void test_adjacent_strict_firstVariableWidth_success() throws Exception {
1125         // succeeds greedily parsing variable width, then fixed width, to non-numeric Z
1126         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('Z').toFormatter(Locale.UK);
1127         ParsePosition pp = new ParsePosition(0);
1128         TemporalAccessor parsed = f.parseUnresolved("12309Z", pp);
1129         assertEquals(pp.getErrorIndex(), -1);
1130         assertEquals(pp.getIndex(), 6);
1131         assertEquals(parsed.getLong(HOUR_OF_DAY), 123L);
1132         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 9L);
1133     }
1134 
1135     @Test
1136     public void test_adjacent_strict_firstVariableWidth_fails() throws Exception {
1137         // fails because literal is a number and variable width parse greedily absorbs it
1138         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK);
1139         ParsePosition pp = new ParsePosition(0);
1140         TemporalAccessor parsed = f.parseUnresolved("12309", pp);
1141         assertEquals(pp.getErrorIndex(), 5);
1142         assertEquals(parsed, null);
1143     }
1144 
1145     @Test
1146     public void test_adjacent_lenient() throws Exception {
1147         // succeeds because both number elements are fixed width even in lenient mode
1148         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK);
1149         ParsePosition pp = new ParsePosition(0);
1150         TemporalAccessor parsed = f.parseUnresolved("12309", pp);
1151         assertEquals(pp.getErrorIndex(), -1);
1152         assertEquals(pp.getIndex(), 5);
1153         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
1154         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
1155     }
1156 
1157     @Test
1158     public void test_adjacent_lenient_firstVariableWidth_success() throws Exception {
1159         // succeeds greedily parsing variable width, then fixed width, to non-numeric Z
1160         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('Z').toFormatter(Locale.UK);
1161         ParsePosition pp = new ParsePosition(0);
1162         TemporalAccessor parsed = f.parseUnresolved("12309Z", pp);
1163         assertEquals(pp.getErrorIndex(), -1);
1164         assertEquals(pp.getIndex(), 6);
1165         assertEquals(parsed.getLong(HOUR_OF_DAY), 123L);
1166         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 9L);
1167     }
1168 
1169     @Test
1170     public void test_adjacent_lenient_firstVariableWidth_fails() throws Exception {
1171         // fails because literal is a number and variable width parse greedily absorbs it
1172         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK);
1173         ParsePosition pp = new ParsePosition(0);
1174         TemporalAccessor parsed = f.parseUnresolved("12309", pp);
1175         assertEquals(pp.getErrorIndex(), 5);
1176         assertEquals(parsed, null);
1177     }
1178 
1179     //-----------------------------------------------------------------------
1180     @Test
1181     public void test_adjacent_strict_fractionFollows() throws Exception {
1182         // succeeds because hour/min are fixed width
1183         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 0, 3, false).toFormatter(Locale.UK);
1184         ParsePosition pp = new ParsePosition(0);
1185         TemporalAccessor parsed = f.parseUnresolved("1230567", pp);
1186         assertEquals(pp.getErrorIndex(), -1);
1187         assertEquals(pp.getIndex(), 7);
1188         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
1189         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
1190         assertEquals(parsed.getLong(NANO_OF_SECOND), 567_000_000L);
1191     }
1192 
1193     @Test
1194     public void test_adjacent_strict_fractionFollows_2digit() throws Exception {
1195         // succeeds because hour/min are fixed width
1196         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 0, 3, false).toFormatter(Locale.UK);
1197         ParsePosition pp = new ParsePosition(0);
1198         TemporalAccessor parsed = f.parseUnresolved("123056", pp);
1199         assertEquals(pp.getErrorIndex(), -1);
1200         assertEquals(pp.getIndex(), 6);
1201         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
1202         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
1203         assertEquals(parsed.getLong(NANO_OF_SECOND), 560_000_000L);
1204     }
1205 
1206     @Test
1207     public void test_adjacent_strict_fractionFollows_0digit() throws Exception {
1208         // succeeds because hour/min are fixed width
1209         DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 0, 3, false).toFormatter(Locale.UK);
1210         ParsePosition pp = new ParsePosition(0);
1211         TemporalAccessor parsed = f.parseUnresolved("1230", pp);
1212         assertEquals(pp.getErrorIndex(), -1);
1213         assertEquals(pp.getIndex(), 4);
1214         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
1215         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
1216     }
1217 
1218     @Test
1219     public void test_adjacent_lenient_fractionFollows() throws Exception {
1220         // succeeds because hour/min are fixed width
1221         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 3, 3, false).toFormatter(Locale.UK);
1222         ParsePosition pp = new ParsePosition(0);
1223         TemporalAccessor parsed = f.parseUnresolved("1230567", pp);
1224         assertEquals(pp.getErrorIndex(), -1);
1225         assertEquals(pp.getIndex(), 7);
1226         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
1227         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
1228         assertEquals(parsed.getLong(NANO_OF_SECOND), 567_000_000L);
1229     }
1230 
1231     @Test
1232     public void test_adjacent_lenient_fractionFollows_2digit() throws Exception {
1233         // succeeds because hour/min are fixed width
1234         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 3, 3, false).toFormatter(Locale.UK);
1235         ParsePosition pp = new ParsePosition(0);
1236         TemporalAccessor parsed = f.parseUnresolved("123056", pp);
1237         assertEquals(pp.getErrorIndex(), -1);
1238         assertEquals(pp.getIndex(), 6);
1239         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
1240         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
1241         assertEquals(parsed.getLong(NANO_OF_SECOND), 560_000_000L);
1242     }
1243 
1244     @Test
1245     public void test_adjacent_lenient_fractionFollows_0digit() throws Exception {
1246         // succeeds because hour, min and fraction of seconds are fixed width
1247         DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 3, 3, false).toFormatter(Locale.UK);
1248         ParsePosition pp = new ParsePosition(0);
1249         TemporalAccessor parsed = f.parseUnresolved("1230", pp);
1250         assertEquals(pp.getErrorIndex(), -1);
1251         assertEquals(pp.getIndex(), 4);
1252         assertEquals(parsed.getLong(HOUR_OF_DAY), 12L);
1253         assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L);
1254     }
1255 
1256     @DataProvider(name="adjacentFractionParseData")
1257     Object[][] data_adjacent_fraction_parse() {
1258         return new Object[][] {
1259             {"20130812214600025", "yyyyMMddHHmmssSSS", LocalDateTime.of(2013, 8, 12, 21, 46, 00, 25000000)},
1260             {"201308122146000256", "yyyyMMddHHmmssSSSS", LocalDateTime.of(2013, 8, 12, 21, 46, 00, 25600000)},
1261         };
1262     }
1263 
1264     @Test(dataProvider = "adjacentFractionParseData")
1265     public void test_adjacent_fraction(String input, String pattern, LocalDateTime expected) {
1266         DateTimeFormatter dtf = DateTimeFormatter.ofPattern(pattern);
1267         LocalDateTime actual = LocalDateTime.parse(input, dtf);
1268         assertEquals(actual, expected);
1269     }
1270 
1271     @DataProvider(name="lenientOffsetParseData")
1272     Object[][] data_lenient_offset_parse() {
1273         return new Object[][] {
1274             {"+HH", "+01", 3600},
1275             {"+HH", "+0101", 3660},
1276             {"+HH", "+010101", 3661},
1277             {"+HH", "+01", 3600},
1278             {"+HH", "+01:01", 3660},
1279             {"+HH", "+01:01:01", 3661},
1280             {"+HHmm", "+01", 3600},
1281             {"+HHmm", "+0101", 3660},
1282             {"+HHmm", "+010101", 3661},
1283             {"+HH:mm", "+01", 3600},
1284             {"+HH:mm", "+01:01", 3660},
1285             {"+HH:mm", "+01:01:01", 3661},
1286             {"+HHMM", "+01", 3600},
1287             {"+HHMM", "+0101", 3660},
1288             {"+HHMM", "+010101", 3661},
1289             {"+HH:MM", "+01", 3600},
1290             {"+HH:MM", "+01:01", 3660},
1291             {"+HH:MM", "+01:01:01", 3661},
1292             {"+HHMMss", "+01", 3600},
1293             {"+HHMMss", "+0101", 3660},
1294             {"+HHMMss", "+010101", 3661},
1295             {"+HH:MM:ss", "+01", 3600},
1296             {"+HH:MM:ss", "+01:01", 3660},
1297             {"+HH:MM:ss", "+01:01:01", 3661},
1298             {"+HHMMSS", "+01", 3600},
1299             {"+HHMMSS", "+0101", 3660},
1300             {"+HHMMSS", "+010101", 3661},
1301             {"+HH:MM:SS", "+01", 3600},
1302             {"+HH:MM:SS", "+01:01", 3660},
1303             {"+HH:MM:SS", "+01:01:01", 3661},
1304             {"+HHmmss", "+01", 3600},
1305             {"+HHmmss", "+0101", 3660},
1306             {"+HHmmss", "+010101", 3661},
1307             {"+HH:mm:ss", "+01", 3600},
1308             {"+HH:mm:ss", "+01:01", 3660},
1309             {"+HH:mm:ss", "+01:01:01", 3661},
1310 
1311             {"+H", "+1", 3600},
1312             {"+H", "+101", 3660},
1313             {"+H", "+10101", 3661},
1314             {"+H", "+1:01", 3660},
1315             {"+H", "+1:01:01", 3661},
1316             {"+H", "+01", 3600},
1317             {"+H", "+0101", 3660},
1318             {"+H", "+010101", 3661},
1319             {"+H", "+01:01", 3660},
1320             {"+H", "+01:01:01", 3661},
1321             {"+Hmm", "+1", 3600},
1322             {"+Hmm", "+101", 3660},
1323             {"+Hmm", "+10101", 3661},
1324             {"+Hmm", "+01", 3600},
1325             {"+Hmm", "+0101", 3660},
1326             {"+Hmm", "+010101", 3661},
1327             {"+H:mm", "+1", 3600},
1328             {"+H:mm", "+1:01", 3660},
1329             {"+H:mm", "+1:01:01", 3661},
1330             {"+H:mm", "+01", 3600},
1331             {"+H:mm", "+01:01", 3660},
1332             {"+H:mm", "+01:01:01", 3661},
1333             {"+HMM", "+1", 3600},
1334             {"+HMM", "+101", 3660},
1335             {"+HMM", "+10101", 3661},
1336             {"+HMM", "+01", 3600},
1337             {"+HMM", "+0101", 3660},
1338             {"+HMM", "+010101", 3661},
1339             {"+H:MM", "+1", 3600},
1340             {"+H:MM", "+1:01", 3660},
1341             {"+H:MM", "+1:01:01", 3661},
1342             {"+H:MM", "+01", 3600},
1343             {"+H:MM", "+01:01", 3660},
1344             {"+H:MM", "+01:01:01", 3661},
1345             {"+HMMss", "+1", 3600},
1346             {"+HMMss", "+101", 3660},
1347             {"+HMMss", "+10101", 3661},
1348             {"+HMMss", "+01", 3600},
1349             {"+HMMss", "+0101", 3660},
1350             {"+HMMss", "+010101", 3661},
1351             {"+H:MM:ss", "+1", 3600},
1352             {"+H:MM:ss", "+1:01", 3660},
1353             {"+H:MM:ss", "+1:01:01", 3661},
1354             {"+H:MM:ss", "+01", 3600},
1355             {"+H:MM:ss", "+01:01", 3660},
1356             {"+H:MM:ss", "+01:01:01", 3661},
1357             {"+HMMSS", "+1", 3600},
1358             {"+HMMSS", "+101", 3660},
1359             {"+HMMSS", "+10101", 3661},
1360             {"+HMMSS", "+01", 3600},
1361             {"+HMMSS", "+0101", 3660},
1362             {"+HMMSS", "+010101", 3661},
1363             {"+H:MM:SS", "+1", 3600},
1364             {"+H:MM:SS", "+1:01", 3660},
1365             {"+H:MM:SS", "+1:01:01", 3661},
1366             {"+H:MM:SS", "+01", 3600},
1367             {"+H:MM:SS", "+01:01", 3660},
1368             {"+H:MM:SS", "+01:01:01", 3661},
1369             {"+Hmmss", "+1", 3600},
1370             {"+Hmmss", "+101", 3660},
1371             {"+Hmmss", "+10101", 3661},
1372             {"+Hmmss", "+01", 3600},
1373             {"+Hmmss", "+0101", 3660},
1374             {"+Hmmss", "+010101", 3661},
1375             {"+H:mm:ss", "+1", 3600},
1376             {"+H:mm:ss", "+1:01", 3660},
1377             {"+H:mm:ss", "+1:01:01", 3661},
1378             {"+H:mm:ss", "+01", 3600},
1379             {"+H:mm:ss", "+01:01", 3660},
1380             {"+H:mm:ss", "+01:01:01", 3661},
1381         };
1382     }
1383 
1384     @DataProvider(name="strictDoubleDigitHourOffsetParseData")
1385     Object[][] data_strictDoubleDigitHour_offset_parse() {
1386         return new Object[][] {
1387             {"+HH", "+01", 3600},
1388             {"+HHmm", "+01", 3600},
1389             {"+HHmm", "+0101", 3660},
1390             {"+HH:mm", "+01", 3600},
1391             {"+HH:mm", "+01:01", 3660},
1392             {"+HHMM", "+0101", 3660},
1393             {"+HH:MM", "+01:01", 3660},
1394             {"+HHMMss", "+0101", 3660},
1395             {"+HHMMss", "+010101", 3661},
1396             {"+HH:MM:ss", "+01:01", 3660},
1397             {"+HH:MM:ss", "+01:01:01", 3661},
1398             {"+HHMMSS", "+010101", 3661},
1399             {"+HH:MM:SS", "+01:01:01", 3661},
1400             {"+HHmmss", "+01", 3600},
1401             {"+HHmmss", "+0101", 3660},
1402             {"+HHmmss", "+010101", 3661},
1403             {"+HH:mm:ss", "+01", 3600},
1404             {"+HH:mm:ss", "+01:01", 3660},
1405             {"+HH:mm:ss", "+01:01:01", 3661},
1406         };
1407     }
1408 
1409     @DataProvider(name="strictSingleDigitHourOffsetParseData")
1410     Object[][] data_strictSingleDigitHour_offset_parse() {
1411         return new Object[][] {
1412             {"+H", "+01", 3600},
1413             {"+H", "+1", 3600},
1414             {"+Hmm", "+01", 3600},
1415             {"+Hmm", "+0101", 3660},
1416             {"+Hmm", "+1", 3600},
1417             {"+Hmm", "+101", 3660},
1418             {"+H:mm", "+01", 3600},
1419             {"+H:mm", "+01:01", 3660},
1420             {"+H:mm", "+1", 3600},
1421             {"+H:mm", "+1:01", 3660},
1422             {"+HMM", "+0101", 3660},
1423             {"+HMM", "+101", 3660},
1424             {"+H:MM", "+01:01", 3660},
1425             {"+H:MM", "+1:01", 3660},
1426             {"+HMMss", "+0101", 3660},
1427             {"+HMMss", "+010101", 3661},
1428             {"+HMMss", "+101", 3660},
1429             {"+HMMss", "+10101", 3661},
1430             {"+H:MM:ss", "+01:01", 3660},
1431             {"+H:MM:ss", "+01:01:01", 3661},
1432             {"+H:MM:ss", "+1:01", 3660},
1433             {"+H:MM:ss", "+1:01:01", 3661},
1434             {"+HMMSS", "+010101", 3661},
1435             {"+HMMSS", "+10101", 3661},
1436             {"+H:MM:SS", "+01:01:01", 3661},
1437             {"+H:MM:SS", "+1:01:01", 3661},
1438             {"+Hmmss", "+01", 3600},
1439             {"+Hmmss", "+0101", 3660},
1440             {"+Hmmss", "+010101", 3661},
1441             {"+Hmmss", "+1", 3600},
1442             {"+Hmmss", "+101", 3660},
1443             {"+Hmmss", "+10101", 3661},
1444             {"+H:mm:ss", "+01", 3600},
1445             {"+H:mm:ss", "+01:01", 3660},
1446             {"+H:mm:ss", "+01:01:01", 3661},
1447             {"+H:mm:ss", "+1", 3600},
1448             {"+H:mm:ss", "+1:01", 3660},
1449             {"+H:mm:ss", "+1:01:01", 3661},
1450         };
1451     }
1452 
1453     @Test(dataProvider="lenientOffsetParseData")
1454     public void test_lenient_offset_parse_1(String pattern, String offset, int offsetSeconds) {
1455         assertEquals(new DateTimeFormatterBuilder().parseLenient().appendOffset(pattern, "Z").toFormatter().parse(offset).get(OFFSET_SECONDS),
1456                      offsetSeconds);
1457     }
1458 
1459     @Test
1460     public void test_lenient_offset_parse_2() {
1461         assertEquals(new DateTimeFormatterBuilder().parseLenient().appendOffsetId().toFormatter().parse("+01").get(OFFSET_SECONDS),
1462                      3600);
1463     }
1464 
1465     @Test(dataProvider="strictDoubleDigitHourOffsetParseData")
1466     public void test_strictDoubleDigitHour_offset_parse_1(String pattern, String offset, int offsetSeconds) {
1467         assertEquals(new DateTimeFormatterBuilder().appendOffset(pattern, "Z").toFormatter()
1468                 .parse(offset).get(OFFSET_SECONDS), offsetSeconds);
1469     }
1470 
1471     @Test(dataProvider="strictDoubleDigitHourOffsetParseData")
1472     public void test_strictDoubleDigitHour_offset_parse_2(String pattern, String offset, int offsetSeconds) {
1473         assertEquals(new DateTimeFormatterBuilder().appendOffset(pattern, "Z")
1474                 .appendLiteral("text").toFormatter().parse(offset + "text").get(OFFSET_SECONDS), offsetSeconds);
1475     }
1476 
1477     @Test(dataProvider="strictSingleDigitHourOffsetParseData")
1478     public void test_strictSingleDigitHour_offset_parse_1(String pattern, String offset, int offsetSeconds) {
1479         assertEquals(new DateTimeFormatterBuilder().appendOffset(pattern, "Z").toFormatter()
1480                 .parse(offset).get(OFFSET_SECONDS), offsetSeconds);
1481     }
1482 
1483     @Test(dataProvider="strictSingleDigitHourOffsetParseData")
1484     public void test_strictSingleDigitHour_offset_parse_2(String pattern, String offset, int offsetSeconds) {
1485         assertEquals(new DateTimeFormatterBuilder().appendOffset(pattern, "Z")
1486                 .appendLiteral("text").toFormatter().parse(offset + "text").get(OFFSET_SECONDS), offsetSeconds);
1487     }
1488 
1489     @DataProvider(name="strictOffsetAdjacentParseValidPatternData")
1490     Object[][] data_strict_offset_adjacentParse_validPattern() {
1491         return new Object[][] {
1492             {"+HH", "+01", 3600},
1493             {"+HHmm", "+0101", 3660},
1494             {"+HH:mm", "+01", 3600},
1495             {"+HH:mm", "+01:01", 3660},
1496             {"+HHMM", "+0101", 3660},
1497             {"+HH:MM", "+01:01", 3660},
1498             {"+HHMMss", "+010101", 3661},
1499             {"+HH:MM:ss", "+01:01", 3660},
1500             {"+HH:MM:ss", "+01:01:01", 3661},
1501             {"+HHMMSS", "+010101", 3661},
1502             {"+HH:MM:SS", "+01:01:01", 3661},
1503             {"+HHmmss", "+010101", 3661},
1504             {"+HH:mm:ss", "+01", 3600},
1505             {"+HH:mm:ss", "+01:01", 3660},
1506             {"+HH:mm:ss", "+01:01:01", 3661},
1507 
1508             {"+H", "+01", 3600},
1509             {"+Hmm", "+0101", 3660},
1510             {"+H:mm", "+01", 3600},
1511             {"+H:mm", "+01:01", 3660},
1512             {"+H:mm", "+1:01", 3660},
1513             {"+HMM", "+0101", 3660},
1514             {"+H:MM", "+01:01", 3660},
1515             {"+H:MM", "+1:01", 3660},
1516             {"+HMMss", "+010101", 3661},
1517             {"+H:MM:ss", "+01:01", 3660},
1518             {"+H:MM:ss", "+01:01:01", 3661},
1519             {"+H:MM:ss", "+1:01", 3660},
1520             {"+H:MM:ss", "+1:01:01", 3661},
1521             {"+HMMSS", "+010101", 3661},
1522             {"+H:MM:SS", "+01:01:01", 3661},
1523             {"+H:MM:SS", "+1:01:01", 3661},
1524             {"+Hmmss", "+010101", 3661},
1525             {"+H:mm:ss", "+01", 3600},
1526             {"+H:mm:ss", "+01:01", 3660},
1527             {"+H:mm:ss", "+01:01:01", 3661},
1528             {"+H:mm:ss", "+1:01", 3660},
1529             {"+H:mm:ss", "+1:01:01", 3661},
1530         };
1531     }
1532 
1533     @Test(dataProvider="strictOffsetAdjacentParseValidPatternData")
1534     public void test_strict_offset_adjacentValidPattern_parse(String pattern, String offset, int offsetSeconds) {
1535         TemporalAccessor tmp = new DateTimeFormatterBuilder().appendOffset(pattern, "Z")
1536                 .appendValue(HOUR_OF_DAY, 2).toFormatter().parse(offset + "12");
1537         assertEquals(tmp.get(OFFSET_SECONDS), offsetSeconds);
1538         assertEquals(tmp.get(HOUR_OF_DAY), 12);
1539     }
1540 
1541     @DataProvider(name="strictOffsetAdjacentParseInvalidPatternData")
1542     Object[][] data_strict_offset_adjacentParse_invalidPattern() {
1543         return new Object[][] {
1544             {"+HHmm", "+01", 3600},
1545             {"+HHMMss", "+0101", 3660},
1546             {"+HHmmss", "+01", 3600},
1547             {"+HHmmss", "+0101", 3660},
1548             {"+H", "+1", 3600},
1549             {"+Hmm", "+01", 3600},
1550             {"+H:mm", "+1", 3600},
1551             {"+Hmm", "+1", 3600},
1552             {"+Hmm", "+101", 3660},
1553             {"+HMM", "+101", 3660},
1554             {"+HMMss", "+0101", 3660},
1555             {"+HMMss", "+101", 3660},
1556             {"+HMMss", "+10101", 3661},
1557             {"+HMMSS", "+10101", 3661},
1558             {"+Hmmss", "+01", 3600},
1559             {"+Hmmss", "+0101", 3660},
1560             {"+Hmmss", "+1", 3600},
1561             {"+Hmmss", "+101", 3660},
1562             {"+Hmmss", "+10101", 3661},
1563             {"+H:mm:ss", "+1", 3600},
1564         };
1565     }
1566 
1567     @Test(dataProvider="strictOffsetAdjacentParseInvalidPatternData", expectedExceptions=DateTimeParseException.class)
1568     public void test_strict_offset_adjacentInvalidPattern_parse(String pattern, String offset, int offsetSeconds) {
1569        new DateTimeFormatterBuilder().appendOffset(pattern, "Z").appendValue(HOUR_OF_DAY, 2)
1570                .toFormatter().parse(offset + "12");
1571     }
1572 
1573     @DataProvider(name="lenientOffsetAdjacentParseValidPatternData")
1574     Object[][] data_lenient_offset_adjacentParse_validPattern() {
1575         return new Object[][] {
1576             {"+HH:mm", "+01", 3600},
1577             {"+HH:mm", "+01:01", 3660},
1578             {"+HH:MM", "+01:01", 3660},
1579             {"+HH:MM:ss", "+01:01", 3660},
1580             {"+HH:MM:ss", "+01:01:01", 3661},
1581             {"+HHMMSS", "+010101", 3661},
1582             {"+HH:MM:SS", "+01:01:01", 3661},
1583             {"+HHmmss", "+010101", 3661},
1584             {"+HH:mm:ss", "+01", 3600},
1585             {"+HH:mm:ss", "+01:01", 3660},
1586             {"+HH:mm:ss", "+01:01:01", 3661},
1587             {"+H:mm", "+01", 3600},
1588             {"+H:mm", "+01:01", 3660},
1589             {"+H:mm", "+1:01", 3660},
1590             {"+H:MM", "+01:01", 3660},
1591             {"+H:MM", "+1:01", 3660},
1592             {"+HMMss", "+010101", 3661},
1593             {"+H:MM:ss", "+01:01", 3660},
1594             {"+H:MM:ss", "+01:01:01", 3661},
1595             {"+H:MM:ss", "+1:01", 3660},
1596             {"+H:MM:ss", "+1:01:01", 3661},
1597             {"+HMMSS", "+010101", 3661},
1598             {"+H:MM:SS", "+01:01:01", 3661},
1599             {"+H:MM:SS", "+1:01:01", 3661},
1600             {"+Hmmss", "+010101", 3661},
1601             {"+H:mm:ss", "+01", 3600},
1602             {"+H:mm:ss", "+01:01", 3660},
1603             {"+H:mm:ss", "+01:01:01", 3661},
1604             {"+H:mm:ss", "+1:01", 3660},
1605             {"+H:mm:ss", "+1:01:01", 3661},
1606         };
1607     }
1608 
1609     @Test(dataProvider="lenientOffsetAdjacentParseValidPatternData")
1610     public void test_lenient_offset_adjacentValidPattern_parse(String pattern, String offset, int offsetSeconds) {
1611         TemporalAccessor tmp = new DateTimeFormatterBuilder().parseLenient()
1612                 .appendOffset(pattern, "Z").appendValue(HOUR_OF_DAY, 2).toFormatter().parse(offset + "12");
1613         assertEquals(tmp.get(OFFSET_SECONDS), offsetSeconds);
1614         assertEquals(tmp.get(HOUR_OF_DAY), 12);
1615     }
1616 
1617     @Test
1618     public void test_lenient_offset_adjacentValidPattern_parse1() {
1619         TemporalAccessor tmp = new DateTimeFormatterBuilder().parseLenient()
1620                 .appendOffset("+HMMSS", "Z").appendValue(HOUR_OF_DAY, 2).toFormatter().parse("+10101" + "12");
1621         //Equivalent to +101011. In lenient mode, offset will parse upto 6 digit if possible.
1622         //It will take 1 digit from HOUR_OF_DAY.
1623         assertEquals(tmp.get(OFFSET_SECONDS), 36611);
1624         assertEquals(tmp.get(HOUR_OF_DAY), 2);
1625     }
1626 
1627   @DataProvider(name="lenientOffsetAdjacentParseInvalidPatternData")
1628     Object[][] data_lenient_offset_adjacentParse_invalidPattern() {
1629         return new Object[][] {
1630             {"+HH", "+01", 3600},
1631             {"+HHmm", "+0101", 3660},
1632             {"+HHMM", "+0101", 3660},
1633             {"+H", "+01", 3600},
1634             {"+Hmm", "+0101", 3660},
1635             {"+HMM", "+0101", 3660},
1636         };
1637     }
1638 
1639     @Test(dataProvider="lenientOffsetAdjacentParseInvalidPatternData", expectedExceptions=DateTimeParseException.class)
1640     public void test_lenient_offset_adjacentInvalidPattern_parse(String pattern, String offset, int offsetSeconds) {
1641        new DateTimeFormatterBuilder().parseLenient().appendOffset(pattern, "Z")
1642                .appendValue(HOUR_OF_DAY, 2).toFormatter().parse(offset + "12");
1643     }
1644 
1645     @DataProvider(name="badValues")
1646     Object[][] data_badOffsetValues() {
1647         return new Object[][] {
1648             {"+HH", "+24"},
1649             {"+HHMM", "-1361"},
1650             {"+HH:MM:ss", "+13:12:66"},
1651             {"+HH:MM:SS", "+24:60:60"},
1652             {"+HHMMSS", "369999"},
1653             {"+H:MM", "+28:12"},
1654         };
1655     }
1656 
1657     @Test(dataProvider="badValues", expectedExceptions=DateTimeParseException.class)
1658     public void test_badOffset_parse(String pattern, String offset) {
1659         new DateTimeFormatterBuilder().appendOffset(pattern, "Z").toFormatter().parse(offset);
1660     }
1661 
1662     @Test(expectedExceptions=DateTimeParseException.class)
1663     public void test_strict_appendOffsetId() {
1664         new DateTimeFormatterBuilder().appendOffsetId().toFormatter().parse("+01");
1665     }
1666 
1667     @Test(expectedExceptions=DateTimeParseException.class)
1668     public void test_strict_appendOffset_1() {
1669         new DateTimeFormatterBuilder().appendOffset("+HH:MM:ss", "Z").toFormatter().parse("+01");
1670     }
1671 
1672     @Test(expectedExceptions=DateTimeParseException.class)
1673     public void test_strict_appendOffset_2() {
1674         new DateTimeFormatterBuilder().appendOffset("+HHMMss", "Z").toFormatter().parse("+01");
1675     }
1676 
1677     @Test(expectedExceptions=DateTimeParseException.class)
1678     public void test_strict_appendOffset_3() {
1679         new DateTimeFormatterBuilder().appendOffset("+H:MM:ss", "Z").toFormatter().parse("+1");
1680     }
1681 
1682     @Test(expectedExceptions=DateTimeParseException.class)
1683     public void test_strict_appendOffset_4() {
1684         new DateTimeFormatterBuilder().appendOffset("+HMMss", "Z").toFormatter().parse("+1");
1685     }
1686 
1687     @Test
1688     public void test_basic_iso_date() {
1689         assertEquals(BASIC_ISO_DATE.parse("20021231+01").get(OFFSET_SECONDS), 3600);
1690         assertEquals(BASIC_ISO_DATE.parse("20021231+0101").get(OFFSET_SECONDS), 3660);
1691     }
1692 
1693 }