1 /*
   2  * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * This file is available under and governed by the GNU General Public
  26  * License version 2 only, as published by the Free Software Foundation.
  27  * However, the following notice accompanied the original version of this
  28  * file:
  29  *
  30  * Copyright (c) 2009-2012, Stephen Colebourne & Michael Nascimento Santos
  31  *
  32  * All rights reserved.
  33  *
  34  * Redistribution and use in source and binary forms, with or without
  35  * modification, are permitted provided that the following conditions are met:
  36  *
  37  *  * Redistributions of source code must retain the above copyright notice,
  38  *    this list of conditions and the following disclaimer.
  39  *
  40  *  * Redistributions in binary form must reproduce the above copyright notice,
  41  *    this list of conditions and the following disclaimer in the documentation
  42  *    and/or other materials provided with the distribution.
  43  *
  44  *  * Neither the name of JSR-310 nor the names of its contributors
  45  *    may be used to endorse or promote products derived from this software
  46  *    without specific prior written permission.
  47  *
  48  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  49  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  50  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  51  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  52  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  53  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  54  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  55  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  56  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  57  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  58  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  59  */
  60 package tck.java.time.format;
  61 
  62 import static java.time.temporal.ChronoField.DAY_OF_MONTH;
  63 import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
  64 import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
  65 import static java.time.temporal.ChronoField.YEAR;
  66 import static org.testng.Assert.assertEquals;
  67 
  68 import java.time.LocalDate;
  69 import java.time.YearMonth;
  70 import java.time.ZoneOffset;
  71 import java.time.format.DateTimeFormatter;
  72 import java.time.format.DateTimeFormatterBuilder;
  73 import java.time.format.SignStyle;
  74 import java.time.format.TextStyle;
  75 import java.time.temporal.Temporal;
  76 import java.util.HashMap;
  77 import java.util.Locale;
  78 import java.util.Map;
  79 
  80 import org.testng.annotations.BeforeMethod;
  81 import org.testng.annotations.DataProvider;
  82 import org.testng.annotations.Test;
  83 
  84 /**
  85  * Test DateTimeFormatterBuilder.
  86  */
  87 @Test
  88 public class TCKDateTimeFormatterBuilder {
  89 
  90     private DateTimeFormatterBuilder builder;
  91 
  92     @BeforeMethod
  93     public void setUp() {
  94         builder = new DateTimeFormatterBuilder();
  95     }
  96 
  97     //-----------------------------------------------------------------------
  98     @Test
  99     public void test_toFormatter_empty() throws Exception {
 100         DateTimeFormatter f = builder.toFormatter();
 101         assertEquals(f.toString(), "");
 102     }
 103 
 104     //-----------------------------------------------------------------------
 105     @Test(expectedExceptions=NullPointerException.class)
 106     public void test_appendValue_1arg_null() throws Exception {
 107         builder.appendValue(null);
 108     }
 109 
 110     //-----------------------------------------------------------------------
 111     @Test(expectedExceptions=NullPointerException.class)
 112     public void test_appendValue_2arg_null() throws Exception {
 113         builder.appendValue(null, 3);
 114     }
 115 
 116     @Test(expectedExceptions=IllegalArgumentException.class)
 117     public void test_appendValue_2arg_widthTooSmall() throws Exception {
 118         builder.appendValue(DAY_OF_MONTH, 0);
 119     }
 120 
 121     @Test(expectedExceptions=IllegalArgumentException.class)
 122     public void test_appendValue_2arg_widthTooBig() throws Exception {
 123         builder.appendValue(DAY_OF_MONTH, 20);
 124     }
 125 
 126     //-----------------------------------------------------------------------
 127     @Test(expectedExceptions=NullPointerException.class)
 128     public void test_appendValue_3arg_nullField() throws Exception {
 129         builder.appendValue(null, 2, 3, SignStyle.NORMAL);
 130     }
 131 
 132     @Test(expectedExceptions=IllegalArgumentException.class)
 133     public void test_appendValue_3arg_minWidthTooSmall() throws Exception {
 134         builder.appendValue(DAY_OF_MONTH, 0, 2, SignStyle.NORMAL);
 135     }
 136 
 137     @Test(expectedExceptions=IllegalArgumentException.class)
 138     public void test_appendValue_3arg_minWidthTooBig() throws Exception {
 139         builder.appendValue(DAY_OF_MONTH, 20, 2, SignStyle.NORMAL);
 140     }
 141 
 142     @Test(expectedExceptions=IllegalArgumentException.class)
 143     public void test_appendValue_3arg_maxWidthTooSmall() throws Exception {
 144         builder.appendValue(DAY_OF_MONTH, 2, 0, SignStyle.NORMAL);
 145     }
 146 
 147     @Test(expectedExceptions=IllegalArgumentException.class)
 148     public void test_appendValue_3arg_maxWidthTooBig() throws Exception {
 149         builder.appendValue(DAY_OF_MONTH, 2, 20, SignStyle.NORMAL);
 150     }
 151 
 152     @Test(expectedExceptions=IllegalArgumentException.class)
 153     public void test_appendValue_3arg_maxWidthMinWidth() throws Exception {
 154         builder.appendValue(DAY_OF_MONTH, 4, 2, SignStyle.NORMAL);
 155     }
 156 
 157     @Test(expectedExceptions=NullPointerException.class)
 158     public void test_appendValue_3arg_nullSignStyle() throws Exception {
 159         builder.appendValue(DAY_OF_MONTH, 2, 3, null);
 160     }
 161 
 162     //-----------------------------------------------------------------------
 163     @Test(expectedExceptions=NullPointerException.class)
 164     public void test_appendValueReduced_null() throws Exception {
 165         builder.appendValueReduced(null, 2, 2000);
 166     }
 167 
 168     //-----------------------------------------------------------------------
 169     //-----------------------------------------------------------------------
 170     //-----------------------------------------------------------------------
 171     @Test(expectedExceptions=NullPointerException.class)
 172     public void test_appendFraction_4arg_nullRule() throws Exception {
 173         builder.appendFraction(null, 1, 9, false);
 174     }
 175 
 176     @Test(expectedExceptions=IllegalArgumentException.class)
 177     public void test_appendFraction_4arg_invalidRuleNotFixedSet() throws Exception {
 178         builder.appendFraction(DAY_OF_MONTH, 1, 9, false);
 179     }
 180 
 181     @Test(expectedExceptions=IllegalArgumentException.class)
 182     public void test_appendFraction_4arg_minTooSmall() throws Exception {
 183         builder.appendFraction(MINUTE_OF_HOUR, -1, 9, false);
 184     }
 185 
 186     @Test(expectedExceptions=IllegalArgumentException.class)
 187     public void test_appendFraction_4arg_minTooBig() throws Exception {
 188         builder.appendFraction(MINUTE_OF_HOUR, 10, 9, false);
 189     }
 190 
 191     @Test(expectedExceptions=IllegalArgumentException.class)
 192     public void test_appendFraction_4arg_maxTooSmall() throws Exception {
 193         builder.appendFraction(MINUTE_OF_HOUR, 0, -1, false);
 194     }
 195 
 196     @Test(expectedExceptions=IllegalArgumentException.class)
 197     public void test_appendFraction_4arg_maxTooBig() throws Exception {
 198         builder.appendFraction(MINUTE_OF_HOUR, 1, 10, false);
 199     }
 200 
 201     @Test(expectedExceptions=IllegalArgumentException.class)
 202     public void test_appendFraction_4arg_maxWidthMinWidth() throws Exception {
 203         builder.appendFraction(MINUTE_OF_HOUR, 9, 3, false);
 204     }
 205 
 206     //-----------------------------------------------------------------------
 207     //-----------------------------------------------------------------------
 208     //-----------------------------------------------------------------------
 209     @Test(expectedExceptions=NullPointerException.class)
 210     public void test_appendText_1arg_null() throws Exception {
 211         builder.appendText(null);
 212     }
 213 
 214     //-----------------------------------------------------------------------
 215     @Test(expectedExceptions=NullPointerException.class)
 216     public void test_appendText_2arg_nullRule() throws Exception {
 217         builder.appendText(null, TextStyle.SHORT);
 218     }
 219 
 220     @Test(expectedExceptions=NullPointerException.class)
 221     public void test_appendText_2arg_nullStyle() throws Exception {
 222         builder.appendText(MONTH_OF_YEAR, (TextStyle) null);
 223     }
 224 
 225     //-----------------------------------------------------------------------
 226     @Test(expectedExceptions=NullPointerException.class)
 227     public void test_appendTextMap_nullRule() throws Exception {
 228         builder.appendText(null, new HashMap<>());
 229     }
 230 
 231     @Test(expectedExceptions=NullPointerException.class)
 232     public void test_appendTextMap_nullStyle() throws Exception {
 233         builder.appendText(MONTH_OF_YEAR, (Map<Long, String>) null);
 234     }
 235 
 236     //-----------------------------------------------------------------------
 237     //-----------------------------------------------------------------------
 238     //-----------------------------------------------------------------------
 239     @DataProvider(name="offsetPatterns")
 240     Object[][] data_offsetPatterns() {
 241         return new Object[][] {
 242                 {"+HH", 2, 0, 0, "+02"},
 243                 {"+HH", -2, 0, 0, "-02"},
 244                 {"+HH", 2, 30, 0, "+02"},
 245                 {"+HH", 2, 0, 45, "+02"},
 246                 {"+HH", 2, 30, 45, "+02"},
 247 
 248                 {"+HHMM", 2, 0, 0, "+0200"},
 249                 {"+HHMM", -2, 0, 0, "-0200"},
 250                 {"+HHMM", 2, 30, 0, "+0230"},
 251                 {"+HHMM", 2, 0, 45, "+0200"},
 252                 {"+HHMM", 2, 30, 45, "+0230"},
 253 
 254                 {"+HH:MM", 2, 0, 0, "+02:00"},
 255                 {"+HH:MM", -2, 0, 0, "-02:00"},
 256                 {"+HH:MM", 2, 30, 0, "+02:30"},
 257                 {"+HH:MM", 2, 0, 45, "+02:00"},
 258                 {"+HH:MM", 2, 30, 45, "+02:30"},
 259 
 260                 {"+HHMMss", 2, 0, 0, "+0200"},
 261                 {"+HHMMss", -2, 0, 0, "-0200"},
 262                 {"+HHMMss", 2, 30, 0, "+0230"},
 263                 {"+HHMMss", 2, 0, 45, "+020045"},
 264                 {"+HHMMss", 2, 30, 45, "+023045"},
 265 
 266                 {"+HH:MM:ss", 2, 0, 0, "+02:00"},
 267                 {"+HH:MM:ss", -2, 0, 0, "-02:00"},
 268                 {"+HH:MM:ss", 2, 30, 0, "+02:30"},
 269                 {"+HH:MM:ss", 2, 0, 45, "+02:00:45"},
 270                 {"+HH:MM:ss", 2, 30, 45, "+02:30:45"},
 271 
 272                 {"+HHMMSS", 2, 0, 0, "+020000"},
 273                 {"+HHMMSS", -2, 0, 0, "-020000"},
 274                 {"+HHMMSS", 2, 30, 0, "+023000"},
 275                 {"+HHMMSS", 2, 0, 45, "+020045"},
 276                 {"+HHMMSS", 2, 30, 45, "+023045"},
 277 
 278                 {"+HH:MM:SS", 2, 0, 0, "+02:00:00"},
 279                 {"+HH:MM:SS", -2, 0, 0, "-02:00:00"},
 280                 {"+HH:MM:SS", 2, 30, 0, "+02:30:00"},
 281                 {"+HH:MM:SS", 2, 0, 45, "+02:00:45"},
 282                 {"+HH:MM:SS", 2, 30, 45, "+02:30:45"},
 283         };
 284     }
 285 
 286     @Test(dataProvider="offsetPatterns")
 287     public void test_appendOffset_format(String pattern, int h, int m, int s, String expected) throws Exception {
 288         builder.appendOffset(pattern, "Z");
 289         DateTimeFormatter f = builder.toFormatter();
 290         ZoneOffset offset = ZoneOffset.ofHoursMinutesSeconds(h, m, s);
 291         assertEquals(f.format(offset), expected);
 292     }
 293 
 294     @Test(dataProvider="offsetPatterns")
 295     public void test_appendOffset_parse(String pattern, int h, int m, int s, String expected) throws Exception {
 296         builder.appendOffset(pattern, "Z");
 297         DateTimeFormatter f = builder.toFormatter();
 298         ZoneOffset parsed = f.parse(expected, ZoneOffset::from);
 299         assertEquals(f.format(parsed), expected);
 300     }
 301 
 302     @DataProvider(name="badOffsetPatterns")
 303     Object[][] data_badOffsetPatterns() {
 304         return new Object[][] {
 305             {"HH"},
 306             {"HHMM"},
 307             {"HH:MM"},
 308             {"HHMMss"},
 309             {"HH:MM:ss"},
 310             {"HHMMSS"},
 311             {"HH:MM:SS"},
 312             {"+H"},
 313             {"+HMM"},
 314             {"+HHM"},
 315             {"+A"},
 316         };
 317     }
 318 
 319     @Test(dataProvider="badOffsetPatterns", expectedExceptions=IllegalArgumentException.class)
 320     public void test_appendOffset_badPattern(String pattern) throws Exception {
 321         builder.appendOffset(pattern, "Z");
 322     }
 323 
 324     @Test(expectedExceptions=NullPointerException.class)
 325     public void test_appendOffset_3arg_nullText() throws Exception {
 326         builder.appendOffset("+HH:MM", null);
 327     }
 328 
 329     @Test(expectedExceptions=NullPointerException.class)
 330     public void test_appendOffset_3arg_nullPattern() throws Exception {
 331         builder.appendOffset(null, "Z");
 332     }
 333 
 334     //-----------------------------------------------------------------------
 335     //-----------------------------------------------------------------------
 336     //-----------------------------------------------------------------------
 337     @Test(expectedExceptions=NullPointerException.class)
 338     public void test_appendZoneText_1arg_nullText() throws Exception {
 339         builder.appendZoneText(null);
 340     }
 341 
 342     //-----------------------------------------------------------------------
 343     //-----------------------------------------------------------------------
 344     //-----------------------------------------------------------------------
 345     @Test
 346     public void test_padNext_1arg() {
 347         builder.appendValue(MONTH_OF_YEAR).appendLiteral(':').padNext(2).appendValue(DAY_OF_MONTH);
 348         assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2: 1");
 349     }
 350 
 351     @Test(expectedExceptions=IllegalArgumentException.class)
 352     public void test_padNext_1arg_invalidWidth() throws Exception {
 353         builder.padNext(0);
 354     }
 355 
 356     //-----------------------------------------------------------------------
 357     @Test
 358     public void test_padNext_2arg_dash() throws Exception {
 359         builder.appendValue(MONTH_OF_YEAR).appendLiteral(':').padNext(2, '-').appendValue(DAY_OF_MONTH);
 360         assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2:-1");
 361     }
 362 
 363     @Test(expectedExceptions=IllegalArgumentException.class)
 364     public void test_padNext_2arg_invalidWidth() throws Exception {
 365         builder.padNext(0, '-');
 366     }
 367 
 368     //-----------------------------------------------------------------------
 369     @Test
 370     public void test_padOptional() throws Exception {
 371         builder.appendValue(MONTH_OF_YEAR).appendLiteral(':')
 372                 .padNext(5).optionalStart().appendValue(DAY_OF_MONTH).optionalEnd()
 373                 .appendLiteral(':').appendValue(YEAR);
 374         assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2:    1:2013");
 375         assertEquals(builder.toFormatter().format(YearMonth.of(2013, 2)), "2:     :2013");
 376     }
 377 
 378     //-----------------------------------------------------------------------
 379     //-----------------------------------------------------------------------
 380     //-----------------------------------------------------------------------
 381     @Test(expectedExceptions=IllegalStateException.class)
 382     public void test_optionalEnd_noStart() throws Exception {
 383         builder.optionalEnd();
 384     }
 385 
 386     //-----------------------------------------------------------------------
 387     //-----------------------------------------------------------------------
 388     //-----------------------------------------------------------------------
 389     @DataProvider(name="validPatterns")
 390     Object[][] dataValid() {
 391         return new Object[][] {
 392             {"'a'"},
 393             {"''"},
 394             {"'!'"},
 395             {"!"},
 396 
 397             {"'hello_people,][)('"},
 398             {"'hi'"},
 399             {"'yyyy'"},
 400             {"''''"},
 401             {"'o''clock'"},
 402 
 403             {"G"},
 404             {"GG"},
 405             {"GGG"},
 406             {"GGGG"},
 407             {"GGGGG"},
 408 
 409             {"y"},
 410             {"yy"},
 411             {"yyy"},
 412             {"yyyy"},
 413             {"yyyyy"},
 414 
 415             {"M"},
 416             {"MM"},
 417             {"MMM"},
 418             {"MMMM"},
 419             {"MMMMM"},
 420 
 421             {"D"},
 422             {"DD"},
 423             {"DDD"},
 424 
 425             {"d"},
 426             {"dd"},
 427             {"ddd"},
 428 
 429             {"F"},
 430             {"FF"},
 431             {"FFF"},
 432 
 433             {"Q"},
 434             {"QQ"},
 435             {"QQQ"},
 436             {"QQQQ"},
 437             {"QQQQQ"},
 438 
 439             {"E"},
 440             {"EE"},
 441             {"EEE"},
 442             {"EEEE"},
 443             {"EEEEE"},
 444 
 445             {"a"},
 446             {"aa"},
 447             {"aaa"},
 448             {"aaaa"},
 449             {"aaaaa"},
 450 
 451             {"H"},
 452             {"HH"},
 453             {"HHH"},
 454 
 455             {"K"},
 456             {"KK"},
 457             {"KKK"},
 458 
 459             {"k"},
 460             {"kk"},
 461             {"kkk"},
 462 
 463             {"h"},
 464             {"hh"},
 465             {"hhh"},
 466 
 467             {"m"},
 468             {"mm"},
 469             {"mmm"},
 470 
 471             {"s"},
 472             {"ss"},
 473             {"sss"},
 474 
 475             {"S"},
 476             {"SS"},
 477             {"SSS"},
 478             {"SSSSSSSSS"},
 479 
 480             {"A"},
 481             {"AA"},
 482             {"AAA"},
 483 
 484             {"n"},
 485             {"nn"},
 486             {"nnn"},
 487 
 488             {"N"},
 489             {"NN"},
 490             {"NNN"},
 491 
 492             {"z"},
 493             {"zz"},
 494             {"zzz"},
 495             {"zzzz"},
 496 
 497             {"VV"},
 498 
 499             {"Z"},
 500             {"ZZ"},
 501             {"ZZZ"},
 502 
 503             {"X"},
 504             {"XX"},
 505             {"XXX"},
 506             {"XXXX"},
 507             {"XXXXX"},
 508 
 509             {"x"},
 510             {"xx"},
 511             {"xxx"},
 512             {"xxxx"},
 513             {"xxxxx"},
 514 
 515             {"ppH"},
 516             {"pppDD"},
 517 
 518             {"yyyy[-MM[-dd"},
 519             {"yyyy[-MM[-dd]]"},
 520             {"yyyy[-MM[]-dd]"},
 521 
 522             {"yyyy-MM-dd'T'HH:mm:ss.SSS"},
 523 
 524             {"e"},
 525             {"w"},
 526             {"W"},
 527             {"WW"},
 528 
 529         };
 530     }
 531 
 532     @Test(dataProvider="validPatterns")
 533     public void test_appendPattern_valid(String input) throws Exception {
 534         builder.appendPattern(input);  // test is for no error here
 535     }
 536 
 537     //-----------------------------------------------------------------------
 538     @DataProvider(name="invalidPatterns")
 539     Object[][] dataInvalid() {
 540         return new Object[][] {
 541             {"'"},
 542             {"'hello"},
 543             {"'hel''lo"},
 544             {"'hello''"},
 545             {"{"},
 546             {"}"},
 547             {"{}"},
 548             {"]"},
 549             {"yyyy]"},
 550             {"yyyy]MM"},
 551             {"yyyy[MM]]"},
 552 
 553             {"MMMMMM"},
 554             {"QQQQQQ"},
 555             {"EEEEEE"},
 556             {"aaaaaa"},
 557             {"ZZZZ"},
 558             {"XXXXXX"},
 559             {"zzzzz"},
 560 
 561             {"RO"},
 562 
 563             {"p"},
 564             {"pp"},
 565             {"p:"},
 566 
 567             {"f"},
 568             {"ff"},
 569             {"f:"},
 570             {"fy"},
 571             {"fa"},
 572             {"fM"},
 573 
 574             {"ww"},
 575             {"ee"},
 576             {"WWW"},
 577         };
 578     }
 579 
 580     @Test(dataProvider="invalidPatterns", expectedExceptions=IllegalArgumentException.class)
 581     public void test_appendPattern_invalid(String input) throws Exception {
 582         builder.appendPattern(input);  // test is for error here
 583     }
 584 
 585     //-----------------------------------------------------------------------
 586     @DataProvider(name="patternPrint")
 587     Object[][] data_patternPrint() {
 588         return new Object[][] {
 589             {"Q", date(2012, 2, 10), "1"},
 590             {"QQ", date(2012, 2, 10), "01"},
 591 //            {"QQQ", date(2012, 2, 10), "Q1"},  // TODO: data for quarters?
 592 //            {"QQQQ", date(2012, 2, 10), "Q1"},
 593 //            {"QQQQQ", date(2012, 2, 10), "Q1"},
 594         };
 595     }
 596 
 597     @Test(dataProvider="patternPrint")
 598     public void test_appendPattern_patternPrint(String input, Temporal temporal, String expected) throws Exception {
 599         DateTimeFormatter f = builder.appendPattern(input).toFormatter(Locale.UK);
 600         String test = f.format(temporal);
 601         assertEquals(test, expected);
 602     }
 603 
 604     private static Temporal date(int y, int m, int d) {
 605         return LocalDate.of(y, m, d);
 606     }
 607 
 608 }