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
setUp()105     public void setUp() {
106         builder = new DateTimeFormatterBuilder();
107     }
108 
109     //-----------------------------------------------------------------------
110     @Test
test_toFormatter_empty()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
test_parseDefaulting_entireDate()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
test_parseDefaulting_yearOptionalMonthOptionalDay()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)
test_parseDefaulting_null()141     public void test_parseDefaulting_null() {
142         builder.parseDefaulting(null, 1);
143     }
144 
145     //-----------------------------------------------------------------------
146     @Test(expectedExceptions=NullPointerException.class)
test_appendValue_1arg_null()147     public void test_appendValue_1arg_null() throws Exception {
148         builder.appendValue(null);
149     }
150 
151     //-----------------------------------------------------------------------
152     @Test(expectedExceptions=NullPointerException.class)
test_appendValue_2arg_null()153     public void test_appendValue_2arg_null() throws Exception {
154         builder.appendValue(null, 3);
155     }
156 
157     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_2arg_widthTooSmall()158     public void test_appendValue_2arg_widthTooSmall() throws Exception {
159         builder.appendValue(DAY_OF_MONTH, 0);
160     }
161 
162     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_2arg_widthTooBig()163     public void test_appendValue_2arg_widthTooBig() throws Exception {
164         builder.appendValue(DAY_OF_MONTH, 20);
165     }
166 
167     //-----------------------------------------------------------------------
168     @Test(expectedExceptions=NullPointerException.class)
test_appendValue_3arg_nullField()169     public void test_appendValue_3arg_nullField() throws Exception {
170         builder.appendValue(null, 2, 3, SignStyle.NORMAL);
171     }
172 
173     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_3arg_minWidthTooSmall()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)
test_appendValue_3arg_minWidthTooBig()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)
test_appendValue_3arg_maxWidthTooSmall()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)
test_appendValue_3arg_maxWidthTooBig()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)
test_appendValue_3arg_maxWidthMinWidth()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)
test_appendValue_3arg_nullSignStyle()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)
test_appendValueReduced_int_nullField()205     public void test_appendValueReduced_int_nullField() throws Exception {
206         builder.appendValueReduced(null, 2, 2, 2000);
207     }
208 
209     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValueReduced_int_minWidthTooSmall()210     public void test_appendValueReduced_int_minWidthTooSmall() throws Exception {
211         builder.appendValueReduced(YEAR, 0, 2, 2000);
212     }
213 
214     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValueReduced_int_minWidthTooBig()215     public void test_appendValueReduced_int_minWidthTooBig() throws Exception {
216         builder.appendValueReduced(YEAR, 11, 2, 2000);
217     }
218 
219     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValueReduced_int_maxWidthTooSmall()220     public void test_appendValueReduced_int_maxWidthTooSmall() throws Exception {
221         builder.appendValueReduced(YEAR, 2, 0, 2000);
222     }
223 
224     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValueReduced_int_maxWidthTooBig()225     public void test_appendValueReduced_int_maxWidthTooBig() throws Exception {
226         builder.appendValueReduced(YEAR, 2, 11, 2000);
227     }
228 
229     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValueReduced_int_maxWidthLessThanMin()230     public void test_appendValueReduced_int_maxWidthLessThanMin() throws Exception {
231         builder.appendValueReduced(YEAR, 2, 1, 2000);
232     }
233 
234     //-----------------------------------------------------------------------
235     @Test(expectedExceptions=NullPointerException.class)
test_appendValueReduced_date_nullField()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)
test_appendValueReduced_date_nullDate()241     public void test_appendValueReduced_date_nullDate() throws Exception {
242         builder.appendValueReduced(YEAR, 2, 2, null);
243     }
244 
245     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValueReduced_date_minWidthTooSmall()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)
test_appendValueReduced_date_minWidthTooBig()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)
test_appendValueReduced_date_maxWidthTooSmall()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)
test_appendValueReduced_date_maxWidthTooBig()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)
test_appendValueReduced_date_maxWidthLessThanMin()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)
test_appendFraction_4arg_nullRule()274     public void test_appendFraction_4arg_nullRule() throws Exception {
275         builder.appendFraction(null, 1, 9, false);
276     }
277 
278     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendFraction_4arg_invalidRuleNotFixedSet()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)
test_appendFraction_4arg_minTooSmall()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)
test_appendFraction_4arg_minTooBig()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)
test_appendFraction_4arg_maxTooSmall()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)
test_appendFraction_4arg_maxTooBig()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)
test_appendFraction_4arg_maxWidthMinWidth()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)
test_appendText_1arg_null()312     public void test_appendText_1arg_null() throws Exception {
313         builder.appendText(null);
314     }
315 
316     //-----------------------------------------------------------------------
317     @Test(expectedExceptions=NullPointerException.class)
test_appendText_2arg_nullRule()318     public void test_appendText_2arg_nullRule() throws Exception {
319         builder.appendText(null, TextStyle.SHORT);
320     }
321 
322     @Test(expectedExceptions=NullPointerException.class)
test_appendText_2arg_nullStyle()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)
test_appendTextMap_nullRule()329     public void test_appendTextMap_nullRule() throws Exception {
330         builder.appendText(null, new HashMap<>());
331     }
332 
333     @Test(expectedExceptions=NullPointerException.class)
test_appendTextMap_nullStyle()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")
data_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")
test_appendOffset_format(String pattern, int h, int m, int s, String expected)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")
test_appendOffset_parse(String pattern, int h, int m, int s, String expected)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")
data_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)
test_appendOffset_badPattern(String pattern)567     public void test_appendOffset_badPattern(String pattern) throws Exception {
568         builder.appendOffset(pattern, "Z");
569     }
570 
571     @Test(expectedExceptions=NullPointerException.class)
test_appendOffset_3arg_nullText()572     public void test_appendOffset_3arg_nullText() throws Exception {
573         builder.appendOffset("+HH:MM", null);
574     }
575 
576     @Test(expectedExceptions=NullPointerException.class)
test_appendOffset_3arg_nullPattern()577     public void test_appendOffset_3arg_nullPattern() throws Exception {
578         builder.appendOffset(null, "Z");
579     }
580 
581     //-----------------------------------------------------------------------
582     //-----------------------------------------------------------------------
583     //-----------------------------------------------------------------------
584     @DataProvider(name = "formatGenericTimeZonePatterns")
data_formatGenericNonLocationPatterns()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")
test_appendZoneText_formatGenericTimeZonePatterns(String pattern, String input, String expected)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")
data_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")
test_appendZoneText_parseGenericTimeZonePatterns(String pattern, LocalDateTime ldt, ZoneId zId, String input)616     public void test_appendZoneText_parseGenericTimeZonePatterns(String pattern, LocalDateTime ldt, ZoneId zId, String input) {
617         DateTimeFormatter df = new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter();
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")
data_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")
test_appendZoneText_parseNonGenricTimeZonePatterns_1(String pattern, LocalDateTime ldt, String expected)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")
data_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")
test_appendZoneText_parseNonGenricTimeZonePatterns_2(String pattern, LocalDateTime ldt, String expected)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)
test_appendZoneText_1arg_nullText()677     public void test_appendZoneText_1arg_nullText() throws Exception {
678         builder.appendZoneText(null);
679     }
680 
681     //-----------------------------------------------------------------------
682     //-----------------------------------------------------------------------
683     //-----------------------------------------------------------------------
684     @Test
test_padNext_1arg()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)
test_padNext_1arg_invalidWidth()691     public void test_padNext_1arg_invalidWidth() throws Exception {
692         builder.padNext(0);
693     }
694 
695     //-----------------------------------------------------------------------
696     @Test
test_padNext_2arg_dash()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)
test_padNext_2arg_invalidWidth()703     public void test_padNext_2arg_invalidWidth() throws Exception {
704         builder.padNext(0, '-');
705     }
706 
707     //-----------------------------------------------------------------------
708     @Test
test_padOptional()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)
test_optionalEnd_noStart()721     public void test_optionalEnd_noStart() throws Exception {
722         builder.optionalEnd();
723     }
724 
725     //-----------------------------------------------------------------------
726     //-----------------------------------------------------------------------
727     //-----------------------------------------------------------------------
728     @DataProvider(name="validPatterns")
dataValid()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")
test_appendPattern_valid(String input)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")
dataInvalid()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)
test_appendPattern_invalid(String input)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")
data_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")
test_appendPattern_patternPrint(String input, Temporal temporal, String expected)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 
date(int y, int m, int d)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")
data_modJuilanFieldPattern()987     Object[][] data_modJuilanFieldPattern() {
988         return new Object[][] {
989             {"g", "1"},
990             {"g", "123456"},
991             {"gggggg", "123456"},
992         };
993     }
994 
995     @Test(dataProvider="modJulianFieldPattern")
test_modJulianFieldPattern(String pattern, String input)996     public void test_modJulianFieldPattern(String pattern, String input) throws Exception {
997         DateTimeFormatter.ofPattern(pattern).parse(input);
998     }
999 
1000     @DataProvider(name="modJulianFieldValues")
data_modJuilanFieldValues()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")
test_modJulianFieldValues(int y, int m, int d, String expected)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")
data_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")
test_dayOfYearFieldValues(int y, int m, int d, String pattern, String expected)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")
data_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")
test_dayOfYearFieldAdjacentValueParsing(String input, LocalDateTime expected)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)
test_dayOfYearFieldInvalidValue()1051     public void test_dayOfYearFieldInvalidValue() {
1052         DateTimeFormatter.ofPattern("DDD").parse("1234");
1053     }
1054 
1055     @Test(expectedExceptions = DateTimeParseException.class)
test_dayOfYearFieldInvalidAdacentValueParsingPattern()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")
data_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")
test_secondsPattern(String pattern, String input, LocalTime expected)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")
data_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")
test_secondsValues(String pattern, int seconds , String expected)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)
test_secondsPatternInvalidAdacentValueParsingPattern()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
test_adjacent_strict_firstFixedWidth()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
test_adjacent_strict_firstVariableWidth_success()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
test_adjacent_strict_firstVariableWidth_fails()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
test_adjacent_lenient()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
test_adjacent_lenient_firstVariableWidth_success()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
test_adjacent_lenient_firstVariableWidth_fails()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
test_adjacent_strict_fractionFollows()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
test_adjacent_strict_fractionFollows_2digit()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
test_adjacent_strict_fractionFollows_0digit()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
test_adjacent_lenient_fractionFollows()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
test_adjacent_lenient_fractionFollows_2digit()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
test_adjacent_lenient_fractionFollows_0digit()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")
data_adjacent_fraction_parse()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")
test_adjacent_fraction(String input, String pattern, LocalDateTime expected)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")
data_lenient_offset_parse()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")
data_strictDoubleDigitHour_offset_parse()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")
data_strictSingleDigitHour_offset_parse()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")
test_lenient_offset_parse_1(String pattern, String offset, int offsetSeconds)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
test_lenient_offset_parse_2()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")
test_strictDoubleDigitHour_offset_parse_1(String pattern, String offset, int offsetSeconds)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")
test_strictDoubleDigitHour_offset_parse_2(String pattern, String offset, int offsetSeconds)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")
test_strictSingleDigitHour_offset_parse_1(String pattern, String offset, int offsetSeconds)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")
test_strictSingleDigitHour_offset_parse_2(String pattern, String offset, int offsetSeconds)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")
data_strict_offset_adjacentParse_validPattern()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")
test_strict_offset_adjacentValidPattern_parse(String pattern, String offset, int offsetSeconds)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")
data_strict_offset_adjacentParse_invalidPattern()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)
test_strict_offset_adjacentInvalidPattern_parse(String pattern, String offset, int offsetSeconds)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")
data_lenient_offset_adjacentParse_validPattern()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")
test_lenient_offset_adjacentValidPattern_parse(String pattern, String offset, int offsetSeconds)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
test_lenient_offset_adjacentValidPattern_parse1()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")
data_lenient_offset_adjacentParse_invalidPattern()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)
test_lenient_offset_adjacentInvalidPattern_parse(String pattern, String offset, int offsetSeconds)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")
data_badOffsetValues()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)
test_badOffset_parse(String pattern, String offset)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)
test_strict_appendOffsetId()1663     public void test_strict_appendOffsetId() {
1664         new DateTimeFormatterBuilder().appendOffsetId().toFormatter().parse("+01");
1665     }
1666 
1667     @Test(expectedExceptions=DateTimeParseException.class)
test_strict_appendOffset_1()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)
test_strict_appendOffset_2()1673     public void test_strict_appendOffset_2() {
1674         new DateTimeFormatterBuilder().appendOffset("+HHMMss", "Z").toFormatter().parse("+01");
1675     }
1676 
1677     @Test(expectedExceptions=DateTimeParseException.class)
test_strict_appendOffset_3()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)
test_strict_appendOffset_4()1683     public void test_strict_appendOffset_4() {
1684         new DateTimeFormatterBuilder().appendOffset("+HMMss", "Z").toFormatter().parse("+1");
1685     }
1686 
1687     @Test
test_basic_iso_date()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 }
1694