• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 test.java.time.format;
61 
62 import static java.time.temporal.ChronoField.DAY_OF_MONTH;
63 import static java.time.temporal.ChronoField.DAY_OF_WEEK;
64 import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
65 import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
66 import static java.time.temporal.ChronoField.YEAR;
67 import static org.testng.Assert.assertEquals;
68 import static org.testng.Assert.assertNotNull;
69 
70 import android.icu.util.VersionInfo;
71 import java.text.ParsePosition;
72 import java.time.LocalDate;
73 import java.time.YearMonth;
74 import java.time.ZoneOffset;
75 import java.time.chrono.Chronology;
76 import java.time.chrono.IsoChronology;
77 import java.time.chrono.JapaneseChronology;
78 import java.time.chrono.MinguoChronology;
79 import java.time.format.DateTimeFormatter;
80 import java.time.format.DateTimeFormatterBuilder;
81 import java.time.format.FormatStyle;
82 import java.time.format.SignStyle;
83 import java.time.format.TextStyle;
84 import java.time.temporal.Temporal;
85 import java.time.temporal.TemporalAccessor;
86 import java.util.HashMap;
87 import java.util.Locale;
88 import java.util.Map;
89 
90 import org.testng.annotations.BeforeMethod;
91 import org.testng.annotations.DataProvider;
92 import org.testng.annotations.Test;
93 
94 /**
95  * Test DateTimeFormatterBuilder.
96  */
97 @Test
98 public class TestDateTimeFormatterBuilder {
99 
100     private DateTimeFormatterBuilder builder;
101 
102     @BeforeMethod
setUp()103     public void setUp() {
104         builder = new DateTimeFormatterBuilder();
105     }
106 
107     //-----------------------------------------------------------------------
108     @Test
test_toFormatter_empty()109     public void test_toFormatter_empty() throws Exception {
110         DateTimeFormatter f = builder.toFormatter();
111         assertEquals(f.toString(), "");
112     }
113 
114     //-----------------------------------------------------------------------
115     @Test
test_parseCaseSensitive()116     public void test_parseCaseSensitive() throws Exception {
117         builder.parseCaseSensitive();
118         DateTimeFormatter f = builder.toFormatter();
119         assertEquals(f.toString(), "ParseCaseSensitive(true)");
120     }
121 
122     @Test
test_parseCaseInsensitive()123     public void test_parseCaseInsensitive() throws Exception {
124         builder.parseCaseInsensitive();
125         DateTimeFormatter f = builder.toFormatter();
126         assertEquals(f.toString(), "ParseCaseSensitive(false)");
127     }
128 
129     //-----------------------------------------------------------------------
130     @Test
test_parseStrict()131     public void test_parseStrict() throws Exception {
132         builder.parseStrict();
133         DateTimeFormatter f = builder.toFormatter();
134         assertEquals(f.toString(), "ParseStrict(true)");
135     }
136 
137     @Test
test_parseLenient()138     public void test_parseLenient() throws Exception {
139         builder.parseLenient();
140         DateTimeFormatter f = builder.toFormatter();
141         assertEquals(f.toString(), "ParseStrict(false)");
142     }
143 
144     //-----------------------------------------------------------------------
145     @Test
test_appendValue_1arg()146     public void test_appendValue_1arg() throws Exception {
147         builder.appendValue(DAY_OF_MONTH);
148         DateTimeFormatter f = builder.toFormatter();
149         assertEquals(f.toString(), "Value(DayOfMonth)");
150     }
151 
152     @Test(expectedExceptions=NullPointerException.class)
test_appendValue_1arg_null()153     public void test_appendValue_1arg_null() throws Exception {
154         builder.appendValue(null);
155     }
156 
157     //-----------------------------------------------------------------------
158     @Test
test_appendValue_2arg()159     public void test_appendValue_2arg() throws Exception {
160         builder.appendValue(DAY_OF_MONTH, 3);
161         DateTimeFormatter f = builder.toFormatter();
162         assertEquals(f.toString(), "Value(DayOfMonth,3)");
163     }
164 
165     @Test(expectedExceptions=NullPointerException.class)
test_appendValue_2arg_null()166     public void test_appendValue_2arg_null() throws Exception {
167         builder.appendValue(null, 3);
168     }
169 
170     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_2arg_widthTooSmall()171     public void test_appendValue_2arg_widthTooSmall() throws Exception {
172         builder.appendValue(DAY_OF_MONTH, 0);
173     }
174 
175     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_2arg_widthTooBig()176     public void test_appendValue_2arg_widthTooBig() throws Exception {
177         builder.appendValue(DAY_OF_MONTH, 20);
178     }
179 
180     //-----------------------------------------------------------------------
181     @Test
test_appendValue_3arg()182     public void test_appendValue_3arg() throws Exception {
183         builder.appendValue(DAY_OF_MONTH, 2, 3, SignStyle.NORMAL);
184         DateTimeFormatter f = builder.toFormatter();
185         assertEquals(f.toString(), "Value(DayOfMonth,2,3,NORMAL)");
186     }
187 
188     @Test(expectedExceptions=NullPointerException.class)
test_appendValue_3arg_nullField()189     public void test_appendValue_3arg_nullField() throws Exception {
190         builder.appendValue(null, 2, 3, SignStyle.NORMAL);
191     }
192 
193     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_3arg_minWidthTooSmall()194     public void test_appendValue_3arg_minWidthTooSmall() throws Exception {
195         builder.appendValue(DAY_OF_MONTH, 0, 2, SignStyle.NORMAL);
196     }
197 
198     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_3arg_minWidthTooBig()199     public void test_appendValue_3arg_minWidthTooBig() throws Exception {
200         builder.appendValue(DAY_OF_MONTH, 20, 2, SignStyle.NORMAL);
201     }
202 
203     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_3arg_maxWidthTooSmall()204     public void test_appendValue_3arg_maxWidthTooSmall() throws Exception {
205         builder.appendValue(DAY_OF_MONTH, 2, 0, SignStyle.NORMAL);
206     }
207 
208     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_3arg_maxWidthTooBig()209     public void test_appendValue_3arg_maxWidthTooBig() throws Exception {
210         builder.appendValue(DAY_OF_MONTH, 2, 20, SignStyle.NORMAL);
211     }
212 
213     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendValue_3arg_maxWidthMinWidth()214     public void test_appendValue_3arg_maxWidthMinWidth() throws Exception {
215         builder.appendValue(DAY_OF_MONTH, 4, 2, SignStyle.NORMAL);
216     }
217 
218     @Test(expectedExceptions=NullPointerException.class)
test_appendValue_3arg_nullSignStyle()219     public void test_appendValue_3arg_nullSignStyle() throws Exception {
220         builder.appendValue(DAY_OF_MONTH, 2, 3, null);
221     }
222 
223     //-----------------------------------------------------------------------
224     @Test
test_appendValue_subsequent2_parse3()225     public void test_appendValue_subsequent2_parse3() throws Exception {
226         builder.appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL).appendValue(DAY_OF_MONTH, 2);
227         DateTimeFormatter f = builder.toFormatter();
228         assertEquals(f.toString(), "Value(MonthOfYear,1,2,NORMAL)Value(DayOfMonth,2)");
229         TemporalAccessor parsed = f.parseUnresolved("123", new ParsePosition(0));
230         assertEquals(parsed.getLong(MONTH_OF_YEAR), 1L);
231         assertEquals(parsed.getLong(DAY_OF_MONTH), 23L);
232     }
233 
234     @Test
test_appendValue_subsequent2_parse4()235     public void test_appendValue_subsequent2_parse4() throws Exception {
236         builder.appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL).appendValue(DAY_OF_MONTH, 2);
237         DateTimeFormatter f = builder.toFormatter();
238         assertEquals(f.toString(), "Value(MonthOfYear,1,2,NORMAL)Value(DayOfMonth,2)");
239         TemporalAccessor parsed = f.parseUnresolved("0123", new ParsePosition(0));
240         assertEquals(parsed.getLong(MONTH_OF_YEAR), 1L);
241         assertEquals(parsed.getLong(DAY_OF_MONTH), 23L);
242     }
243 
244     @Test
test_appendValue_subsequent2_parse5()245     public void test_appendValue_subsequent2_parse5() throws Exception {
246         builder.appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL).appendValue(DAY_OF_MONTH, 2).appendLiteral('4');
247         DateTimeFormatter f = builder.toFormatter();
248         assertEquals(f.toString(), "Value(MonthOfYear,1,2,NORMAL)Value(DayOfMonth,2)'4'");
249         TemporalAccessor parsed = f.parseUnresolved("01234", new ParsePosition(0));
250         assertEquals(parsed.getLong(MONTH_OF_YEAR), 1L);
251         assertEquals(parsed.getLong(DAY_OF_MONTH), 23L);
252     }
253 
254     @Test
test_appendValue_subsequent3_parse6()255     public void test_appendValue_subsequent3_parse6() throws Exception {
256         builder
257             .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
258             .appendValue(MONTH_OF_YEAR, 2)
259             .appendValue(DAY_OF_MONTH, 2);
260         DateTimeFormatter f = builder.toFormatter();
261         assertEquals(f.toString(), "Value(Year,4,10,EXCEEDS_PAD)Value(MonthOfYear,2)Value(DayOfMonth,2)");
262         TemporalAccessor parsed = f.parseUnresolved("20090630", new ParsePosition(0));
263         assertEquals(parsed.getLong(YEAR), 2009L);
264         assertEquals(parsed.getLong(MONTH_OF_YEAR), 6L);
265         assertEquals(parsed.getLong(DAY_OF_MONTH), 30L);
266     }
267 
268     //-----------------------------------------------------------------------
269     @Test(expectedExceptions=NullPointerException.class)
test_appendValueReduced_null()270     public void test_appendValueReduced_null() throws Exception {
271         builder.appendValueReduced(null, 2, 2, 2000);
272     }
273 
274     @Test
test_appendValueReduced()275     public void test_appendValueReduced() throws Exception {
276         builder.appendValueReduced(YEAR, 2, 2, 2000);
277         DateTimeFormatter f = builder.toFormatter();
278         assertEquals(f.toString(), "ReducedValue(Year,2,2,2000)");
279         TemporalAccessor parsed = f.parseUnresolved("12", new ParsePosition(0));
280         assertEquals(parsed.getLong(YEAR), 2012L);
281     }
282 
283     @Test
test_appendValueReduced_subsequent_parse()284     public void test_appendValueReduced_subsequent_parse() throws Exception {
285         builder.appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL).appendValueReduced(YEAR, 2, 2, 2000);
286         DateTimeFormatter f = builder.toFormatter();
287         assertEquals(f.toString(), "Value(MonthOfYear,1,2,NORMAL)ReducedValue(Year,2,2,2000)");
288         ParsePosition ppos = new ParsePosition(0);
289         TemporalAccessor parsed = f.parseUnresolved("123", ppos);
290         assertNotNull(parsed, "Parse failed: " + ppos.toString());
291         assertEquals(parsed.getLong(MONTH_OF_YEAR), 1L);
292         assertEquals(parsed.getLong(YEAR), 2023L);
293     }
294 
295     //-----------------------------------------------------------------------
296     //-----------------------------------------------------------------------
297     //-----------------------------------------------------------------------
298     @Test
test_appendFraction_4arg()299     public void test_appendFraction_4arg() throws Exception {
300         builder.appendFraction(MINUTE_OF_HOUR, 1, 9, false);
301         DateTimeFormatter f = builder.toFormatter();
302         assertEquals(f.toString(), "Fraction(MinuteOfHour,1,9)");
303     }
304 
305     @Test(expectedExceptions=NullPointerException.class)
test_appendFraction_4arg_nullRule()306     public void test_appendFraction_4arg_nullRule() throws Exception {
307         builder.appendFraction(null, 1, 9, false);
308     }
309 
310     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendFraction_4arg_invalidRuleNotFixedSet()311     public void test_appendFraction_4arg_invalidRuleNotFixedSet() throws Exception {
312         builder.appendFraction(DAY_OF_MONTH, 1, 9, false);
313     }
314 
315     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendFraction_4arg_minTooSmall()316     public void test_appendFraction_4arg_minTooSmall() throws Exception {
317         builder.appendFraction(MINUTE_OF_HOUR, -1, 9, false);
318     }
319 
320     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendFraction_4arg_minTooBig()321     public void test_appendFraction_4arg_minTooBig() throws Exception {
322         builder.appendFraction(MINUTE_OF_HOUR, 10, 9, false);
323     }
324 
325     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendFraction_4arg_maxTooSmall()326     public void test_appendFraction_4arg_maxTooSmall() throws Exception {
327         builder.appendFraction(MINUTE_OF_HOUR, 0, -1, false);
328     }
329 
330     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendFraction_4arg_maxTooBig()331     public void test_appendFraction_4arg_maxTooBig() throws Exception {
332         builder.appendFraction(MINUTE_OF_HOUR, 1, 10, false);
333     }
334 
335     @Test(expectedExceptions=IllegalArgumentException.class)
test_appendFraction_4arg_maxWidthMinWidth()336     public void test_appendFraction_4arg_maxWidthMinWidth() throws Exception {
337         builder.appendFraction(MINUTE_OF_HOUR, 9, 3, false);
338     }
339 
340     //-----------------------------------------------------------------------
341     //-----------------------------------------------------------------------
342     //-----------------------------------------------------------------------
343     @Test
test_appendText_1arg()344     public void test_appendText_1arg() throws Exception {
345         builder.appendText(MONTH_OF_YEAR);
346         DateTimeFormatter f = builder.toFormatter();
347         assertEquals(f.toString(), "Text(MonthOfYear)");
348     }
349 
350     @Test(expectedExceptions=NullPointerException.class)
test_appendText_1arg_null()351     public void test_appendText_1arg_null() throws Exception {
352         builder.appendText(null);
353     }
354 
355     //-----------------------------------------------------------------------
356     @Test
test_appendText_2arg()357     public void test_appendText_2arg() throws Exception {
358         builder.appendText(MONTH_OF_YEAR, TextStyle.SHORT);
359         DateTimeFormatter f = builder.toFormatter();
360         assertEquals(f.toString(), "Text(MonthOfYear,SHORT)");
361     }
362 
363     @Test(expectedExceptions=NullPointerException.class)
test_appendText_2arg_nullRule()364     public void test_appendText_2arg_nullRule() throws Exception {
365         builder.appendText(null, TextStyle.SHORT);
366     }
367 
368     @Test(expectedExceptions=NullPointerException.class)
test_appendText_2arg_nullStyle()369     public void test_appendText_2arg_nullStyle() throws Exception {
370         builder.appendText(MONTH_OF_YEAR, (TextStyle) null);
371     }
372 
373     //-----------------------------------------------------------------------
374     @Test
test_appendTextMap()375     public void test_appendTextMap() throws Exception {
376         Map<Long, String> map = new HashMap<>();
377         map.put(1L, "JNY");
378         map.put(2L, "FBY");
379         map.put(3L, "MCH");
380         map.put(4L, "APL");
381         map.put(5L, "MAY");
382         map.put(6L, "JUN");
383         map.put(7L, "JLY");
384         map.put(8L, "AGT");
385         map.put(9L, "SPT");
386         map.put(10L, "OBR");
387         map.put(11L, "NVR");
388         map.put(12L, "DBR");
389         builder.appendText(MONTH_OF_YEAR, map);
390         DateTimeFormatter f = builder.toFormatter();
391         assertEquals(f.toString(), "Text(MonthOfYear)");  // TODO: toString should be different?
392     }
393 
394     @Test(expectedExceptions=NullPointerException.class)
test_appendTextMap_nullRule()395     public void test_appendTextMap_nullRule() throws Exception {
396         builder.appendText(null, new HashMap<Long, String>());
397     }
398 
399     @Test(expectedExceptions=NullPointerException.class)
test_appendTextMap_nullStyle()400     public void test_appendTextMap_nullStyle() throws Exception {
401         builder.appendText(MONTH_OF_YEAR, (Map<Long, String>) null);
402     }
403 
404     //-----------------------------------------------------------------------
405     //-----------------------------------------------------------------------
406     //-----------------------------------------------------------------------
407     @Test
test_appendOffsetId()408     public void test_appendOffsetId() throws Exception {
409         builder.appendOffsetId();
410         DateTimeFormatter f = builder.toFormatter();
411         assertEquals(f.toString(), "Offset(+HH:MM:ss,'Z')");
412     }
413 
414     @DataProvider(name="offsetPatterns")
data_offsetPatterns()415     Object[][] data_offsetPatterns() {
416         return new Object[][] {
417                 {"+HH", 2, 0, 0, "+02"},
418                 {"+HH", -2, 0, 0, "-02"},
419                 {"+HH", 2, 30, 0, "+02"},
420                 {"+HH", 2, 0, 45, "+02"},
421                 {"+HH", 2, 30, 45, "+02"},
422 
423                 {"+HHMM", 2, 0, 0, "+0200"},
424                 {"+HHMM", -2, 0, 0, "-0200"},
425                 {"+HHMM", 2, 30, 0, "+0230"},
426                 {"+HHMM", 2, 0, 45, "+0200"},
427                 {"+HHMM", 2, 30, 45, "+0230"},
428 
429                 {"+HH:MM", 2, 0, 0, "+02:00"},
430                 {"+HH:MM", -2, 0, 0, "-02:00"},
431                 {"+HH:MM", 2, 30, 0, "+02:30"},
432                 {"+HH:MM", 2, 0, 45, "+02:00"},
433                 {"+HH:MM", 2, 30, 45, "+02:30"},
434 
435                 {"+HHMMss", 2, 0, 0, "+0200"},
436                 {"+HHMMss", -2, 0, 0, "-0200"},
437                 {"+HHMMss", 2, 30, 0, "+0230"},
438                 {"+HHMMss", 2, 0, 45, "+020045"},
439                 {"+HHMMss", 2, 30, 45, "+023045"},
440 
441                 {"+HH:MM:ss", 2, 0, 0, "+02:00"},
442                 {"+HH:MM:ss", -2, 0, 0, "-02:00"},
443                 {"+HH:MM:ss", 2, 30, 0, "+02:30"},
444                 {"+HH:MM:ss", 2, 0, 45, "+02:00:45"},
445                 {"+HH:MM:ss", 2, 30, 45, "+02:30:45"},
446 
447                 {"+HHMMSS", 2, 0, 0, "+020000"},
448                 {"+HHMMSS", -2, 0, 0, "-020000"},
449                 {"+HHMMSS", 2, 30, 0, "+023000"},
450                 {"+HHMMSS", 2, 0, 45, "+020045"},
451                 {"+HHMMSS", 2, 30, 45, "+023045"},
452 
453                 {"+HH:MM:SS", 2, 0, 0, "+02:00:00"},
454                 {"+HH:MM:SS", -2, 0, 0, "-02:00:00"},
455                 {"+HH:MM:SS", 2, 30, 0, "+02:30:00"},
456                 {"+HH:MM:SS", 2, 0, 45, "+02:00:45"},
457                 {"+HH:MM:SS", 2, 30, 45, "+02:30:45"},
458         };
459     }
460 
461     @Test(dataProvider="offsetPatterns")
test_appendOffset_format(String pattern, int h, int m, int s, String expected)462     public void test_appendOffset_format(String pattern, int h, int m, int s, String expected) throws Exception {
463         builder.appendOffset(pattern, "Z");
464         DateTimeFormatter f = builder.toFormatter();
465         ZoneOffset offset = ZoneOffset.ofHoursMinutesSeconds(h, m, s);
466         assertEquals(f.format(offset), expected);
467     }
468 
469     @Test(dataProvider="offsetPatterns")
test_appendOffset_parse(String pattern, int h, int m, int s, String expected)470     public void test_appendOffset_parse(String pattern, int h, int m, int s, String expected) throws Exception {
471         builder.appendOffset(pattern, "Z");
472         DateTimeFormatter f = builder.toFormatter();
473         ZoneOffset offset = ZoneOffset.ofHoursMinutesSeconds(h, m, s);
474         ZoneOffset parsed = f.parse(expected, ZoneOffset::from);
475         assertEquals(f.format(parsed), expected);
476     }
477 
478     @DataProvider(name="badOffsetPatterns")
data_badOffsetPatterns()479     Object[][] data_badOffsetPatterns() {
480         return new Object[][] {
481             {"HH"},
482             {"HHMM"},
483             {"HH:MM"},
484             {"HHMMss"},
485             {"HH:MM:ss"},
486             {"HHMMSS"},
487             {"HH:MM:SS"},
488             {"+HHM"},
489             {"+A"},
490         };
491     }
492 
493     @Test(dataProvider="badOffsetPatterns", expectedExceptions=IllegalArgumentException.class)
test_appendOffset_badPattern(String pattern)494     public void test_appendOffset_badPattern(String pattern) throws Exception {
495         builder.appendOffset(pattern, "Z");
496     }
497 
498     @Test(expectedExceptions=NullPointerException.class)
test_appendOffset_3arg_nullText()499     public void test_appendOffset_3arg_nullText() throws Exception {
500         builder.appendOffset("+HH:MM", null);
501     }
502 
503     @Test(expectedExceptions=NullPointerException.class)
test_appendOffset_3arg_nullPattern()504     public void test_appendOffset_3arg_nullPattern() throws Exception {
505         builder.appendOffset(null, "Z");
506     }
507 
508     //-----------------------------------------------------------------------
509     //-----------------------------------------------------------------------
510     //-----------------------------------------------------------------------
511     @Test
test_appendZoneId()512     public void test_appendZoneId() throws Exception {
513         builder.appendZoneId();
514         DateTimeFormatter f = builder.toFormatter();
515         assertEquals(f.toString(), "ZoneId()");
516     }
517 
518     @Test
test_appendZoneText_1arg()519     public void test_appendZoneText_1arg() throws Exception {
520         builder.appendZoneText(TextStyle.FULL);
521         DateTimeFormatter f = builder.toFormatter();
522         assertEquals(f.toString(), "ZoneText(FULL)");
523     }
524 
525     @Test(expectedExceptions=NullPointerException.class)
test_appendZoneText_1arg_nullText()526     public void test_appendZoneText_1arg_nullText() throws Exception {
527         builder.appendZoneText(null);
528     }
529 
530     //-----------------------------------------------------------------------
531     //-----------------------------------------------------------------------
532     //-----------------------------------------------------------------------
533     @Test
test_padNext_1arg()534     public void test_padNext_1arg() {
535         builder.appendValue(MONTH_OF_YEAR).appendLiteral(':').padNext(2).appendValue(DAY_OF_MONTH);
536         assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2: 1");
537     }
538 
539     @Test(expectedExceptions=IllegalArgumentException.class)
test_padNext_1arg_invalidWidth()540     public void test_padNext_1arg_invalidWidth() throws Exception {
541         builder.padNext(0);
542     }
543 
544     //-----------------------------------------------------------------------
545     @Test
test_padNext_2arg_dash()546     public void test_padNext_2arg_dash() throws Exception {
547         builder.appendValue(MONTH_OF_YEAR).appendLiteral(':').padNext(2, '-').appendValue(DAY_OF_MONTH);
548         assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2:-1");
549     }
550 
551     @Test(expectedExceptions=IllegalArgumentException.class)
test_padNext_2arg_invalidWidth()552     public void test_padNext_2arg_invalidWidth() throws Exception {
553         builder.padNext(0, '-');
554     }
555 
556     //-----------------------------------------------------------------------
557     @Test
test_padOptional()558     public void test_padOptional() throws Exception {
559         builder.appendValue(MONTH_OF_YEAR).appendLiteral(':')
560                 .padNext(5).optionalStart().appendValue(DAY_OF_MONTH).optionalEnd()
561                 .appendLiteral(':').appendValue(YEAR);
562         assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2:    1:2013");
563         assertEquals(builder.toFormatter().format(YearMonth.of(2013, 2)), "2:     :2013");
564     }
565 
566     //-----------------------------------------------------------------------
567     //-----------------------------------------------------------------------
568     //-----------------------------------------------------------------------
569     @Test
test_optionalStart_noEnd()570     public void test_optionalStart_noEnd() throws Exception {
571         builder.appendValue(MONTH_OF_YEAR).optionalStart().appendValue(DAY_OF_MONTH).appendValue(DAY_OF_WEEK);
572         DateTimeFormatter f = builder.toFormatter();
573         assertEquals(f.toString(), "Value(MonthOfYear)[Value(DayOfMonth)Value(DayOfWeek)]");
574     }
575 
576     @Test
test_optionalStart2_noEnd()577     public void test_optionalStart2_noEnd() throws Exception {
578         builder.appendValue(MONTH_OF_YEAR).optionalStart().appendValue(DAY_OF_MONTH).optionalStart().appendValue(DAY_OF_WEEK);
579         DateTimeFormatter f = builder.toFormatter();
580         assertEquals(f.toString(), "Value(MonthOfYear)[Value(DayOfMonth)[Value(DayOfWeek)]]");
581     }
582 
583     @Test
test_optionalStart_doubleStart()584     public void test_optionalStart_doubleStart() throws Exception {
585         builder.appendValue(MONTH_OF_YEAR).optionalStart().optionalStart().appendValue(DAY_OF_MONTH);
586         DateTimeFormatter f = builder.toFormatter();
587         assertEquals(f.toString(), "Value(MonthOfYear)[[Value(DayOfMonth)]]");
588     }
589 
590     //-----------------------------------------------------------------------
591     @Test
test_optionalEnd()592     public void test_optionalEnd() throws Exception {
593         builder.appendValue(MONTH_OF_YEAR).optionalStart().appendValue(DAY_OF_MONTH).optionalEnd().appendValue(DAY_OF_WEEK);
594         DateTimeFormatter f = builder.toFormatter();
595         assertEquals(f.toString(), "Value(MonthOfYear)[Value(DayOfMonth)]Value(DayOfWeek)");
596     }
597 
598     @Test
test_optionalEnd2()599     public void test_optionalEnd2() throws Exception {
600         builder.appendValue(MONTH_OF_YEAR).optionalStart().appendValue(DAY_OF_MONTH)
601             .optionalStart().appendValue(DAY_OF_WEEK).optionalEnd().appendValue(DAY_OF_MONTH).optionalEnd();
602         DateTimeFormatter f = builder.toFormatter();
603         assertEquals(f.toString(), "Value(MonthOfYear)[Value(DayOfMonth)[Value(DayOfWeek)]Value(DayOfMonth)]");
604     }
605 
606     @Test
test_optionalEnd_doubleStartSingleEnd()607     public void test_optionalEnd_doubleStartSingleEnd() throws Exception {
608         builder.appendValue(MONTH_OF_YEAR).optionalStart().optionalStart().appendValue(DAY_OF_MONTH).optionalEnd();
609         DateTimeFormatter f = builder.toFormatter();
610         assertEquals(f.toString(), "Value(MonthOfYear)[[Value(DayOfMonth)]]");
611     }
612 
613     @Test
test_optionalEnd_doubleStartDoubleEnd()614     public void test_optionalEnd_doubleStartDoubleEnd() throws Exception {
615         builder.appendValue(MONTH_OF_YEAR).optionalStart().optionalStart().appendValue(DAY_OF_MONTH).optionalEnd().optionalEnd();
616         DateTimeFormatter f = builder.toFormatter();
617         assertEquals(f.toString(), "Value(MonthOfYear)[[Value(DayOfMonth)]]");
618     }
619 
620     @Test
test_optionalStartEnd_immediateStartEnd()621     public void test_optionalStartEnd_immediateStartEnd() throws Exception {
622         builder.appendValue(MONTH_OF_YEAR).optionalStart().optionalEnd().appendValue(DAY_OF_MONTH);
623         DateTimeFormatter f = builder.toFormatter();
624         assertEquals(f.toString(), "Value(MonthOfYear)Value(DayOfMonth)");
625     }
626 
627     @Test(expectedExceptions=IllegalStateException.class)
test_optionalEnd_noStart()628     public void test_optionalEnd_noStart() throws Exception {
629         builder.optionalEnd();
630     }
631 
632     //-----------------------------------------------------------------------
633     //-----------------------------------------------------------------------
634     //-----------------------------------------------------------------------
635     @DataProvider(name="validPatterns")
dataValid()636     Object[][] dataValid() {
637         return new Object[][] {
638             {"'a'", "'a'"},
639             {"''", "''"},
640             {"'!'", "'!'"},
641             {"!", "'!'"},
642 
643             {"'hello_people,][)('", "'hello_people,][)('"},
644             {"'hi'", "'hi'"},
645             {"'yyyy'", "'yyyy'"},
646             {"''''", "''"},
647             {"'o''clock'", "'o''clock'"},
648 
649             {"G", "Text(Era,SHORT)"},
650             {"GG", "Text(Era,SHORT)"},
651             {"GGG", "Text(Era,SHORT)"},
652             {"GGGG", "Text(Era)"},
653             {"GGGGG", "Text(Era,NARROW)"},
654 
655             {"u", "Value(Year)"},
656             {"uu", "ReducedValue(Year,2,2,2000-01-01)"},
657             {"uuu", "Value(Year,3,19,NORMAL)"},
658             {"uuuu", "Value(Year,4,19,EXCEEDS_PAD)"},
659             {"uuuuu", "Value(Year,5,19,EXCEEDS_PAD)"},
660 
661             {"y", "Value(YearOfEra)"},
662             {"yy", "ReducedValue(YearOfEra,2,2,2000-01-01)"},
663             {"yyy", "Value(YearOfEra,3,19,NORMAL)"},
664             {"yyyy", "Value(YearOfEra,4,19,EXCEEDS_PAD)"},
665             {"yyyyy", "Value(YearOfEra,5,19,EXCEEDS_PAD)"},
666 
667             {"Y", "Localized(WeekBasedYear)"},
668             {"YY", "Localized(ReducedValue(WeekBasedYear,2,2,2000-01-01))"},
669             {"YYY", "Localized(WeekBasedYear,3,19,NORMAL)"},
670             {"YYYY", "Localized(WeekBasedYear,4,19,EXCEEDS_PAD)"},
671             {"YYYYY", "Localized(WeekBasedYear,5,19,EXCEEDS_PAD)"},
672 
673             {"M", "Value(MonthOfYear)"},
674             {"MM", "Value(MonthOfYear,2)"},
675             {"MMM", "Text(MonthOfYear,SHORT)"},
676             {"MMMM", "Text(MonthOfYear)"},
677             {"MMMMM", "Text(MonthOfYear,NARROW)"},
678 
679             {"L", "Value(MonthOfYear)"},
680             {"LL", "Value(MonthOfYear,2)"},
681             {"LLL", "Text(MonthOfYear,SHORT_STANDALONE)"},
682             {"LLLL", "Text(MonthOfYear,FULL_STANDALONE)"},
683             {"LLLLL", "Text(MonthOfYear,NARROW_STANDALONE)"},
684 
685             {"D", "Value(DayOfYear)"},
686             {"DD", "Value(DayOfYear,2,3,NOT_NEGATIVE)"},
687             {"DDD", "Value(DayOfYear,3)"},
688 
689             {"d", "Value(DayOfMonth)"},
690             {"dd", "Value(DayOfMonth,2)"},
691 
692             {"F", "Value(AlignedDayOfWeekInMonth)"},
693 
694             {"Q", "Value(QuarterOfYear)"},
695             {"QQ", "Value(QuarterOfYear,2)"},
696             {"QQQ", "Text(QuarterOfYear,SHORT)"},
697             {"QQQQ", "Text(QuarterOfYear)"},
698             {"QQQQQ", "Text(QuarterOfYear,NARROW)"},
699 
700             {"q", "Value(QuarterOfYear)"},
701             {"qq", "Value(QuarterOfYear,2)"},
702             {"qqq", "Text(QuarterOfYear,SHORT_STANDALONE)"},
703             {"qqqq", "Text(QuarterOfYear,FULL_STANDALONE)"},
704             {"qqqqq", "Text(QuarterOfYear,NARROW_STANDALONE)"},
705 
706             {"E", "Text(DayOfWeek,SHORT)"},
707             {"EE", "Text(DayOfWeek,SHORT)"},
708             {"EEE", "Text(DayOfWeek,SHORT)"},
709             {"EEEE", "Text(DayOfWeek)"},
710             {"EEEEE", "Text(DayOfWeek,NARROW)"},
711 
712             {"e", "Localized(DayOfWeek,1)"},
713             {"ee", "Localized(DayOfWeek,2)"},
714             {"eee", "Text(DayOfWeek,SHORT)"},
715             {"eeee", "Text(DayOfWeek)"},
716             {"eeeee", "Text(DayOfWeek,NARROW)"},
717 
718             {"c", "Localized(DayOfWeek,1)"},
719             {"ccc", "Text(DayOfWeek,SHORT_STANDALONE)"},
720             {"cccc", "Text(DayOfWeek,FULL_STANDALONE)"},
721             {"ccccc", "Text(DayOfWeek,NARROW_STANDALONE)"},
722 
723             {"a", "Text(AmPmOfDay,SHORT)"},
724 
725             {"H", "Value(HourOfDay)"},
726             {"HH", "Value(HourOfDay,2)"},
727 
728             {"K", "Value(HourOfAmPm)"},
729             {"KK", "Value(HourOfAmPm,2)"},
730 
731             {"k", "Value(ClockHourOfDay)"},
732             {"kk", "Value(ClockHourOfDay,2)"},
733 
734             {"h", "Value(ClockHourOfAmPm)"},
735             {"hh", "Value(ClockHourOfAmPm,2)"},
736 
737             {"m", "Value(MinuteOfHour)"},
738             {"mm", "Value(MinuteOfHour,2)"},
739 
740             {"s", "Value(SecondOfMinute)"},
741             {"ss", "Value(SecondOfMinute,2)"},
742 
743             {"S", "Fraction(NanoOfSecond,1,1)"},
744             {"SS", "Fraction(NanoOfSecond,2,2)"},
745             {"SSS", "Fraction(NanoOfSecond,3,3)"},
746             {"SSSSSSSSS", "Fraction(NanoOfSecond,9,9)"},
747 
748             {"A", "Value(MilliOfDay,1,19,NOT_NEGATIVE)"},
749             {"AA", "Value(MilliOfDay,2,19,NOT_NEGATIVE)"},
750             {"AAA", "Value(MilliOfDay,3,19,NOT_NEGATIVE)"},
751 
752             {"n", "Value(NanoOfSecond,1,19,NOT_NEGATIVE)"},
753             {"nn", "Value(NanoOfSecond,2,19,NOT_NEGATIVE)"},
754             {"nnn", "Value(NanoOfSecond,3,19,NOT_NEGATIVE)"},
755 
756             {"N", "Value(NanoOfDay,1,19,NOT_NEGATIVE)"},
757             {"NN", "Value(NanoOfDay,2,19,NOT_NEGATIVE)"},
758             {"NNN", "Value(NanoOfDay,3,19,NOT_NEGATIVE)"},
759 
760             {"z", "ZoneText(SHORT)"},
761             {"zz", "ZoneText(SHORT)"},
762             {"zzz", "ZoneText(SHORT)"},
763             {"zzzz", "ZoneText(FULL)"},
764 
765             {"VV", "ZoneId()"},
766 
767             {"Z", "Offset(+HHMM,'+0000')"},  // SimpleDateFormat
768             {"ZZ", "Offset(+HHMM,'+0000')"},  // SimpleDateFormat
769             {"ZZZ", "Offset(+HHMM,'+0000')"},  // SimpleDateFormat
770 
771             {"X", "Offset(+HHmm,'Z')"},  // LDML/almost SimpleDateFormat
772             {"XX", "Offset(+HHMM,'Z')"},  // LDML/SimpleDateFormat
773             {"XXX", "Offset(+HH:MM,'Z')"},  // LDML/SimpleDateFormat
774             {"XXXX", "Offset(+HHMMss,'Z')"},  // LDML
775             {"XXXXX", "Offset(+HH:MM:ss,'Z')"},  // LDML
776 
777             {"x", "Offset(+HHmm,'+00')"},  // LDML
778             {"xx", "Offset(+HHMM,'+0000')"},  // LDML
779             {"xxx", "Offset(+HH:MM,'+00:00')"},  // LDML
780             {"xxxx", "Offset(+HHMMss,'+0000')"},  // LDML
781             {"xxxxx", "Offset(+HH:MM:ss,'+00:00')"},  // LDML
782 
783             {"ppH", "Pad(Value(HourOfDay),2)"},
784             {"pppDD", "Pad(Value(DayOfYear,2,3,NOT_NEGATIVE),3)"},
785 
786             {"yyyy[-MM[-dd", "Value(YearOfEra,4,19,EXCEEDS_PAD)['-'Value(MonthOfYear,2)['-'Value(DayOfMonth,2)]]"},
787             {"yyyy[-MM[-dd]]", "Value(YearOfEra,4,19,EXCEEDS_PAD)['-'Value(MonthOfYear,2)['-'Value(DayOfMonth,2)]]"},
788             {"yyyy[-MM[]-dd]", "Value(YearOfEra,4,19,EXCEEDS_PAD)['-'Value(MonthOfYear,2)'-'Value(DayOfMonth,2)]"},
789 
790             {"yyyy-MM-dd'T'HH:mm:ss.SSS", "Value(YearOfEra,4,19,EXCEEDS_PAD)'-'Value(MonthOfYear,2)'-'Value(DayOfMonth,2)" +
791                 "'T'Value(HourOfDay,2)':'Value(MinuteOfHour,2)':'Value(SecondOfMinute,2)'.'Fraction(NanoOfSecond,3,3)"},
792 
793             {"w", "Localized(WeekOfWeekBasedYear,1)"},
794             {"ww", "Localized(WeekOfWeekBasedYear,2)"},
795             {"W", "Localized(WeekOfMonth,1)"},
796         };
797     }
798 
799     @Test(dataProvider="validPatterns")
test_appendPattern_valid(String input, String expected)800     public void test_appendPattern_valid(String input, String expected) throws Exception {
801         builder.appendPattern(input);
802         DateTimeFormatter f = builder.toFormatter();
803         assertEquals(f.toString(), expected);
804     }
805 
806     //-----------------------------------------------------------------------
807     @DataProvider(name="invalidPatterns")
dataInvalid()808     Object[][] dataInvalid() {
809         return new Object[][] {
810             {"'"},
811             {"'hello"},
812             {"'hel''lo"},
813             {"'hello''"},
814             {"{"},
815             {"}"},
816             {"{}"},
817             {"]"},
818             {"yyyy]"},
819             {"yyyy]MM"},
820             {"yyyy[MM]]"},
821 
822             {"aa"},
823             {"aaa"},
824             {"aaaa"},
825             {"aaaaa"},
826             {"aaaaaa"},
827             {"MMMMMM"},
828             {"LLLLLL"},
829             {"QQQQQQ"},
830             {"qqqqqq"},
831             {"EEEEEE"},
832             {"eeeeee"},
833             {"cc"},
834             {"cccccc"},
835             {"ddd"},
836             {"DDDD"},
837             {"FF"},
838             {"FFF"},
839             {"hhh"},
840             {"HHH"},
841             {"kkk"},
842             {"KKK"},
843             {"mmm"},
844             {"sss"},
845             {"OO"},
846             {"OOO"},
847             {"OOOOO"},
848             {"XXXXXX"},
849             {"ZZZZZZ"},
850             {"zzzzz"},
851             {"V"},
852             {"VVV"},
853             {"VVVV"},
854             {"VVVVV"},
855 
856             {"RO"},
857 
858             {"p"},
859             {"pp"},
860             {"p:"},
861 
862             {"f"},
863             {"ff"},
864             {"f:"},
865             {"fy"},
866             {"fa"},
867             {"fM"},
868 
869             {"www"},
870             {"WW"},
871         };
872     }
873 
874     @Test(dataProvider="invalidPatterns", expectedExceptions=IllegalArgumentException.class)
test_appendPattern_invalid(String input)875     public void test_appendPattern_invalid(String input) throws Exception {
876         try {
877             builder.appendPattern(input);
878         } catch (IllegalArgumentException ex) {
879             throw ex;
880         }
881     }
882 
883     //-----------------------------------------------------------------------
884     @DataProvider(name="localePatterns")
localizedDateTimePatterns()885     Object[][] localizedDateTimePatterns() {
886         // Android-changed: Adapt for changes since old CLDR version this tests were written for.
887         return new Object[][] {
888             {FormatStyle.FULL, FormatStyle.FULL, IsoChronology.INSTANCE, Locale.US, "EEEE, MMMM d, y 'at' h:mm:ss a zzzz"},
889             {FormatStyle.LONG, FormatStyle.LONG, IsoChronology.INSTANCE, Locale.US, "MMMM d, y 'at' h:mm:ss a z"},
890             {FormatStyle.MEDIUM, FormatStyle.MEDIUM, IsoChronology.INSTANCE, Locale.US, "MMM d, y, h:mm:ss a"},
891             {FormatStyle.SHORT, FormatStyle.SHORT, IsoChronology.INSTANCE, Locale.US, "M/d/yy, h:mm a"},
892             {FormatStyle.FULL, null, IsoChronology.INSTANCE, Locale.US, "EEEE, MMMM d, y"},
893             {FormatStyle.LONG, null, IsoChronology.INSTANCE, Locale.US, "MMMM d, y"},
894             {FormatStyle.MEDIUM, null, IsoChronology.INSTANCE, Locale.US, "MMM d, y"},
895             {FormatStyle.SHORT, null, IsoChronology.INSTANCE, Locale.US, "M/d/yy"},
896             {null, FormatStyle.FULL, IsoChronology.INSTANCE, Locale.US, "h:mm:ss a zzzz"},
897             {null, FormatStyle.LONG, IsoChronology.INSTANCE, Locale.US, "h:mm:ss a z"},
898             {null, FormatStyle.MEDIUM, IsoChronology.INSTANCE, Locale.US, "h:mm:ss a"},
899             {null, FormatStyle.SHORT, IsoChronology.INSTANCE, Locale.US, "h:mm a"},
900         };
901     }
902 
903     @Test(dataProvider="localePatterns")
test_getLocalizedDateTimePattern(FormatStyle dateStyle, FormatStyle timeStyle, Chronology chrono, Locale locale, String expected)904     public void test_getLocalizedDateTimePattern(FormatStyle dateStyle, FormatStyle timeStyle,
905             Chronology chrono, Locale locale, String expected) {
906         // Android-changed: Require ICU 70 to pass the test due to CLDR data change
907         if (VersionInfo.ICU_VERSION.getMajor() < 70) {
908             return;
909         }
910         String actual = DateTimeFormatterBuilder.getLocalizedDateTimePattern(dateStyle, timeStyle, chrono, locale);
911         assertEquals(actual, expected, "Pattern " + convertNonAscii(actual));
912     }
913 
914     @Test(expectedExceptions=java.lang.IllegalArgumentException.class)
test_getLocalizedDateTimePatternIAE()915     public void test_getLocalizedDateTimePatternIAE() {
916         DateTimeFormatterBuilder.getLocalizedDateTimePattern(null, null, IsoChronology.INSTANCE, Locale.US);
917     }
918 
919     @Test(expectedExceptions=java.lang.NullPointerException.class)
test_getLocalizedChronoNPE()920     public void test_getLocalizedChronoNPE() {
921         DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.SHORT, FormatStyle.SHORT, null, Locale.US);
922     }
923 
924     @Test(expectedExceptions=java.lang.NullPointerException.class)
test_getLocalizedLocaleNPE()925     public void test_getLocalizedLocaleNPE() {
926         DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.SHORT, FormatStyle.SHORT, IsoChronology.INSTANCE, null);
927     }
928 
929     /**
930      * Returns a string that includes non-ascii characters after expanding
931      * the non-ascii characters to their Java language \\uxxxx form.
932      * @param input an input string
933      * @return the encoded string.
934      */
convertNonAscii(String input)935     private String convertNonAscii(String input) {
936         StringBuilder sb = new StringBuilder(input.length() * 6);
937         for (int i = 0; i < input.length(); i++) {
938             char ch = input.charAt(i);
939             if (ch < 255) {
940                 sb.append(ch);
941             } else {
942                 sb.append("\\u");
943                 sb.append(Integer.toHexString(ch));
944             }
945         }
946         return sb.toString();
947     }
948 
date(int y, int m, int d)949     private static Temporal date(int y, int m, int d) {
950         return LocalDate.of(y, m, d);
951     }
952 }
953