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.YearMonth; 76 import java.time.ZoneOffset; 77 import java.time.format.DateTimeFormatter; 78 import java.time.format.DateTimeFormatterBuilder; 79 import java.time.format.DateTimeParseException; 80 import java.time.format.SignStyle; 81 import java.time.format.TextStyle; 82 import java.time.temporal.Temporal; 83 import java.time.temporal.TemporalAccessor; 84 import java.util.HashMap; 85 import java.util.Locale; 86 import java.util.Map; 87 88 import org.testng.annotations.BeforeMethod; 89 import org.testng.annotations.DataProvider; 90 import org.testng.annotations.Test; 91 92 /** 93 * Test DateTimeFormatterBuilder. 94 */ 95 @Test 96 public class TCKDateTimeFormatterBuilder { 97 98 private DateTimeFormatterBuilder builder; 99 100 @BeforeMethod 101 public void setUp() { 102 builder = new DateTimeFormatterBuilder(); 103 } 104 105 //----------------------------------------------------------------------- 106 @Test 107 public void test_toFormatter_empty() throws Exception { 108 DateTimeFormatter f = builder.toFormatter(); 109 assertEquals(f.format(LocalDate.of(2012, 6, 30)), ""); 110 } 111 112 //----------------------------------------------------------------------- 113 @Test 114 public void test_parseDefaulting_entireDate() { 115 DateTimeFormatter f = builder 116 .parseDefaulting(YEAR, 2012).parseDefaulting(MONTH_OF_YEAR, 6) 117 .parseDefaulting(DAY_OF_MONTH, 30).toFormatter(); 118 LocalDate parsed = f.parse("", LocalDate::from); // blank string can be parsed 119 assertEquals(parsed, LocalDate.of(2012, 6, 30)); 120 } 121 122 @Test 123 public void test_parseDefaulting_yearOptionalMonthOptionalDay() { 124 DateTimeFormatter f = builder 125 .appendValue(YEAR) 126 .optionalStart().appendLiteral('-').appendValue(MONTH_OF_YEAR) 127 .optionalStart().appendLiteral('-').appendValue(DAY_OF_MONTH) 128 .optionalEnd().optionalEnd() 129 .parseDefaulting(MONTH_OF_YEAR, 1) 130 .parseDefaulting(DAY_OF_MONTH, 1).toFormatter(); 131 assertEquals(f.parse("2012", LocalDate::from), LocalDate.of(2012, 1, 1)); 132 assertEquals(f.parse("2012-6", LocalDate::from), LocalDate.of(2012, 6, 1)); 133 assertEquals(f.parse("2012-6-30", LocalDate::from), LocalDate.of(2012, 6, 30)); 134 } 135 136 @Test(expectedExceptions = NullPointerException.class) 137 public void test_parseDefaulting_null() { 138 builder.parseDefaulting(null, 1); 139 } 140 141 //----------------------------------------------------------------------- 142 @Test(expectedExceptions=NullPointerException.class) 143 public void test_appendValue_1arg_null() throws Exception { 144 builder.appendValue(null); 145 } 146 147 //----------------------------------------------------------------------- 148 @Test(expectedExceptions=NullPointerException.class) 149 public void test_appendValue_2arg_null() throws Exception { 150 builder.appendValue(null, 3); 151 } 152 153 @Test(expectedExceptions=IllegalArgumentException.class) 154 public void test_appendValue_2arg_widthTooSmall() throws Exception { 155 builder.appendValue(DAY_OF_MONTH, 0); 156 } 157 158 @Test(expectedExceptions=IllegalArgumentException.class) 159 public void test_appendValue_2arg_widthTooBig() throws Exception { 160 builder.appendValue(DAY_OF_MONTH, 20); 161 } 162 163 //----------------------------------------------------------------------- 164 @Test(expectedExceptions=NullPointerException.class) 165 public void test_appendValue_3arg_nullField() throws Exception { 166 builder.appendValue(null, 2, 3, SignStyle.NORMAL); 167 } 168 169 @Test(expectedExceptions=IllegalArgumentException.class) 170 public void test_appendValue_3arg_minWidthTooSmall() throws Exception { 171 builder.appendValue(DAY_OF_MONTH, 0, 2, SignStyle.NORMAL); 172 } 173 174 @Test(expectedExceptions=IllegalArgumentException.class) 175 public void test_appendValue_3arg_minWidthTooBig() throws Exception { 176 builder.appendValue(DAY_OF_MONTH, 20, 2, SignStyle.NORMAL); 177 } 178 179 @Test(expectedExceptions=IllegalArgumentException.class) 180 public void test_appendValue_3arg_maxWidthTooSmall() throws Exception { 181 builder.appendValue(DAY_OF_MONTH, 2, 0, SignStyle.NORMAL); 182 } 183 184 @Test(expectedExceptions=IllegalArgumentException.class) 185 public void test_appendValue_3arg_maxWidthTooBig() throws Exception { 186 builder.appendValue(DAY_OF_MONTH, 2, 20, SignStyle.NORMAL); 187 } 188 189 @Test(expectedExceptions=IllegalArgumentException.class) 190 public void test_appendValue_3arg_maxWidthMinWidth() throws Exception { 191 builder.appendValue(DAY_OF_MONTH, 4, 2, SignStyle.NORMAL); 192 } 193 194 @Test(expectedExceptions=NullPointerException.class) 195 public void test_appendValue_3arg_nullSignStyle() throws Exception { 196 builder.appendValue(DAY_OF_MONTH, 2, 3, null); 197 } 198 199 //----------------------------------------------------------------------- 200 @Test(expectedExceptions=NullPointerException.class) 201 public void test_appendValueReduced_int_nullField() throws Exception { 202 builder.appendValueReduced(null, 2, 2, 2000); 203 } 204 205 @Test(expectedExceptions=IllegalArgumentException.class) 206 public void test_appendValueReduced_int_minWidthTooSmall() throws Exception { 207 builder.appendValueReduced(YEAR, 0, 2, 2000); 208 } 209 210 @Test(expectedExceptions=IllegalArgumentException.class) 211 public void test_appendValueReduced_int_minWidthTooBig() throws Exception { 212 builder.appendValueReduced(YEAR, 11, 2, 2000); 213 } 214 215 @Test(expectedExceptions=IllegalArgumentException.class) 216 public void test_appendValueReduced_int_maxWidthTooSmall() throws Exception { 217 builder.appendValueReduced(YEAR, 2, 0, 2000); 218 } 219 220 @Test(expectedExceptions=IllegalArgumentException.class) 221 public void test_appendValueReduced_int_maxWidthTooBig() throws Exception { 222 builder.appendValueReduced(YEAR, 2, 11, 2000); 223 } 224 225 @Test(expectedExceptions=IllegalArgumentException.class) 226 public void test_appendValueReduced_int_maxWidthLessThanMin() throws Exception { 227 builder.appendValueReduced(YEAR, 2, 1, 2000); 228 } 229 230 //----------------------------------------------------------------------- 231 @Test(expectedExceptions=NullPointerException.class) 232 public void test_appendValueReduced_date_nullField() throws Exception { 233 builder.appendValueReduced(null, 2, 2, LocalDate.of(2000, 1, 1)); 234 } 235 236 @Test(expectedExceptions=NullPointerException.class) 237 public void test_appendValueReduced_date_nullDate() throws Exception { 238 builder.appendValueReduced(YEAR, 2, 2, null); 239 } 240 241 @Test(expectedExceptions=IllegalArgumentException.class) 242 public void test_appendValueReduced_date_minWidthTooSmall() throws Exception { 243 builder.appendValueReduced(YEAR, 0, 2, LocalDate.of(2000, 1, 1)); 244 } 245 246 @Test(expectedExceptions=IllegalArgumentException.class) 247 public void test_appendValueReduced_date_minWidthTooBig() throws Exception { 248 builder.appendValueReduced(YEAR, 11, 2, LocalDate.of(2000, 1, 1)); 249 } 250 251 @Test(expectedExceptions=IllegalArgumentException.class) 252 public void test_appendValueReduced_date_maxWidthTooSmall() throws Exception { 253 builder.appendValueReduced(YEAR, 2, 0, LocalDate.of(2000, 1, 1)); 254 } 255 256 @Test(expectedExceptions=IllegalArgumentException.class) 257 public void test_appendValueReduced_date_maxWidthTooBig() throws Exception { 258 builder.appendValueReduced(YEAR, 2, 11, LocalDate.of(2000, 1, 1)); 259 } 260 261 @Test(expectedExceptions=IllegalArgumentException.class) 262 public void test_appendValueReduced_date_maxWidthLessThanMin() throws Exception { 263 builder.appendValueReduced(YEAR, 2, 1, LocalDate.of(2000, 1, 1)); 264 } 265 266 //----------------------------------------------------------------------- 267 //----------------------------------------------------------------------- 268 //----------------------------------------------------------------------- 269 @Test(expectedExceptions=NullPointerException.class) 270 public void test_appendFraction_4arg_nullRule() throws Exception { 271 builder.appendFraction(null, 1, 9, false); 272 } 273 274 @Test(expectedExceptions=IllegalArgumentException.class) 275 public void test_appendFraction_4arg_invalidRuleNotFixedSet() throws Exception { 276 builder.appendFraction(DAY_OF_MONTH, 1, 9, false); 277 } 278 279 @Test(expectedExceptions=IllegalArgumentException.class) 280 public void test_appendFraction_4arg_minTooSmall() throws Exception { 281 builder.appendFraction(MINUTE_OF_HOUR, -1, 9, false); 282 } 283 284 @Test(expectedExceptions=IllegalArgumentException.class) 285 public void test_appendFraction_4arg_minTooBig() throws Exception { 286 builder.appendFraction(MINUTE_OF_HOUR, 10, 9, false); 287 } 288 289 @Test(expectedExceptions=IllegalArgumentException.class) 290 public void test_appendFraction_4arg_maxTooSmall() throws Exception { 291 builder.appendFraction(MINUTE_OF_HOUR, 0, -1, false); 292 } 293 294 @Test(expectedExceptions=IllegalArgumentException.class) 295 public void test_appendFraction_4arg_maxTooBig() throws Exception { 296 builder.appendFraction(MINUTE_OF_HOUR, 1, 10, false); 297 } 298 299 @Test(expectedExceptions=IllegalArgumentException.class) 300 public void test_appendFraction_4arg_maxWidthMinWidth() throws Exception { 301 builder.appendFraction(MINUTE_OF_HOUR, 9, 3, false); 302 } 303 304 //----------------------------------------------------------------------- 305 //----------------------------------------------------------------------- 306 //----------------------------------------------------------------------- 307 @Test(expectedExceptions=NullPointerException.class) 308 public void test_appendText_1arg_null() throws Exception { 309 builder.appendText(null); 310 } 311 312 //----------------------------------------------------------------------- 313 @Test(expectedExceptions=NullPointerException.class) 314 public void test_appendText_2arg_nullRule() throws Exception { 315 builder.appendText(null, TextStyle.SHORT); 316 } 317 318 @Test(expectedExceptions=NullPointerException.class) 319 public void test_appendText_2arg_nullStyle() throws Exception { 320 builder.appendText(MONTH_OF_YEAR, (TextStyle) null); 321 } 322 323 //----------------------------------------------------------------------- 324 @Test(expectedExceptions=NullPointerException.class) 325 public void test_appendTextMap_nullRule() throws Exception { 326 builder.appendText(null, new HashMap<>()); 327 } 328 329 @Test(expectedExceptions=NullPointerException.class) 330 public void test_appendTextMap_nullStyle() throws Exception { 331 builder.appendText(MONTH_OF_YEAR, (Map<Long, String>) null); 332 } 333 334 //----------------------------------------------------------------------- 335 //----------------------------------------------------------------------- 336 //----------------------------------------------------------------------- 337 @DataProvider(name="offsetPatterns") 338 Object[][] data_offsetPatterns() { 339 return new Object[][] { 340 {"+HH", 2, 0, 0, "+02"}, 341 {"+HH", -2, 0, 0, "-02"}, 342 {"+HH", 2, 30, 0, "+02"}, 343 {"+HH", 2, 0, 45, "+02"}, 344 {"+HH", 2, 30, 45, "+02"}, 345 346 {"+HHmm", 2, 0, 0, "+02"}, 347 {"+HHmm", -2, 0, 0, "-02"}, 348 {"+HHmm", 2, 30, 0, "+0230"}, 349 {"+HHmm", 2, 0, 45, "+02"}, 350 {"+HHmm", 2, 30, 45, "+0230"}, 351 352 {"+HH:mm", 2, 0, 0, "+02"}, 353 {"+HH:mm", -2, 0, 0, "-02"}, 354 {"+HH:mm", 2, 30, 0, "+02:30"}, 355 {"+HH:mm", 2, 0, 45, "+02"}, 356 {"+HH:mm", 2, 30, 45, "+02:30"}, 357 358 {"+HHMM", 2, 0, 0, "+0200"}, 359 {"+HHMM", -2, 0, 0, "-0200"}, 360 {"+HHMM", 2, 30, 0, "+0230"}, 361 {"+HHMM", 2, 0, 45, "+0200"}, 362 {"+HHMM", 2, 30, 45, "+0230"}, 363 364 {"+HH:MM", 2, 0, 0, "+02:00"}, 365 {"+HH:MM", -2, 0, 0, "-02:00"}, 366 {"+HH:MM", 2, 30, 0, "+02:30"}, 367 {"+HH:MM", 2, 0, 45, "+02:00"}, 368 {"+HH:MM", 2, 30, 45, "+02:30"}, 369 370 {"+HHMMss", 2, 0, 0, "+0200"}, 371 {"+HHMMss", -2, 0, 0, "-0200"}, 372 {"+HHMMss", 2, 30, 0, "+0230"}, 373 {"+HHMMss", 2, 0, 45, "+020045"}, 374 {"+HHMMss", 2, 30, 45, "+023045"}, 375 376 {"+HH:MM:ss", 2, 0, 0, "+02:00"}, 377 {"+HH:MM:ss", -2, 0, 0, "-02:00"}, 378 {"+HH:MM:ss", 2, 30, 0, "+02:30"}, 379 {"+HH:MM:ss", 2, 0, 45, "+02:00:45"}, 380 {"+HH:MM:ss", 2, 30, 45, "+02:30:45"}, 381 382 {"+HHMMSS", 2, 0, 0, "+020000"}, 383 {"+HHMMSS", -2, 0, 0, "-020000"}, 384 {"+HHMMSS", 2, 30, 0, "+023000"}, 385 {"+HHMMSS", 2, 0, 45, "+020045"}, 386 {"+HHMMSS", 2, 30, 45, "+023045"}, 387 388 {"+HH:MM:SS", 2, 0, 0, "+02:00:00"}, 389 {"+HH:MM:SS", -2, 0, 0, "-02:00:00"}, 390 {"+HH:MM:SS", 2, 30, 0, "+02:30:00"}, 391 {"+HH:MM:SS", 2, 0, 45, "+02:00:45"}, 392 {"+HH:MM:SS", 2, 30, 45, "+02:30:45"}, 393 394 {"+HHmmss", 2, 0, 0, "+02"}, 395 {"+HHmmss", -2, 0, 0, "-02"}, 396 {"+HHmmss", 2, 30, 0, "+0230"}, 397 {"+HHmmss", 2, 0, 45, "+020045"}, 398 {"+HHmmss", 2, 30, 45, "+023045"}, 399 400 {"+HH:mm:ss", 2, 0, 0, "+02"}, 401 {"+HH:mm:ss", -2, 0, 0, "-02"}, 402 {"+HH:mm:ss", 2, 30, 0, "+02:30"}, 403 {"+HH:mm:ss", 2, 0, 45, "+02:00:45"}, 404 {"+HH:mm:ss", 2, 30, 45, "+02:30:45"}, 405 406 407 }; 408 } 409 410 @Test(dataProvider="offsetPatterns") 411 public void test_appendOffset_format(String pattern, int h, int m, int s, String expected) throws Exception { 412 builder.appendOffset(pattern, "Z"); 413 DateTimeFormatter f = builder.toFormatter(); 414 ZoneOffset offset = ZoneOffset.ofHoursMinutesSeconds(h, m, s); 415 assertEquals(f.format(offset), expected); 416 } 417 418 @Test(dataProvider="offsetPatterns") 419 public void test_appendOffset_parse(String pattern, int h, int m, int s, String expected) throws Exception { 420 builder.appendOffset(pattern, "Z"); 421 DateTimeFormatter f = builder.toFormatter(); 422 ZoneOffset parsed = f.parse(expected, ZoneOffset::from); 423 assertEquals(f.format(parsed), expected); 424 } 425 426 @DataProvider(name="badOffsetPatterns") 427 Object[][] data_badOffsetPatterns() { 428 return new Object[][] { 429 {"HH"}, 430 {"HHMM"}, 431 {"HH:MM"}, 432 {"HHMMss"}, 433 {"HH:MM:ss"}, 434 {"HHMMSS"}, 435 {"HH:MM:SS"}, 436 {"+H"}, 437 {"+HMM"}, 438 {"+HHM"}, 439 {"+A"}, 440 }; 441 } 442 443 @Test(dataProvider="badOffsetPatterns", expectedExceptions=IllegalArgumentException.class) 444 public void test_appendOffset_badPattern(String pattern) throws Exception { 445 builder.appendOffset(pattern, "Z"); 446 } 447 448 @Test(expectedExceptions=NullPointerException.class) 449 public void test_appendOffset_3arg_nullText() throws Exception { 450 builder.appendOffset("+HH:MM", null); 451 } 452 453 @Test(expectedExceptions=NullPointerException.class) 454 public void test_appendOffset_3arg_nullPattern() throws Exception { 455 builder.appendOffset(null, "Z"); 456 } 457 458 //----------------------------------------------------------------------- 459 //----------------------------------------------------------------------- 460 //----------------------------------------------------------------------- 461 @Test(expectedExceptions=NullPointerException.class) 462 public void test_appendZoneText_1arg_nullText() throws Exception { 463 builder.appendZoneText(null); 464 } 465 466 //----------------------------------------------------------------------- 467 //----------------------------------------------------------------------- 468 //----------------------------------------------------------------------- 469 @Test 470 public void test_padNext_1arg() { 471 builder.appendValue(MONTH_OF_YEAR).appendLiteral(':').padNext(2).appendValue(DAY_OF_MONTH); 472 assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2: 1"); 473 } 474 475 @Test(expectedExceptions=IllegalArgumentException.class) 476 public void test_padNext_1arg_invalidWidth() throws Exception { 477 builder.padNext(0); 478 } 479 480 //----------------------------------------------------------------------- 481 @Test 482 public void test_padNext_2arg_dash() throws Exception { 483 builder.appendValue(MONTH_OF_YEAR).appendLiteral(':').padNext(2, '-').appendValue(DAY_OF_MONTH); 484 assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2:-1"); 485 } 486 487 @Test(expectedExceptions=IllegalArgumentException.class) 488 public void test_padNext_2arg_invalidWidth() throws Exception { 489 builder.padNext(0, '-'); 490 } 491 492 //----------------------------------------------------------------------- 493 @Test 494 public void test_padOptional() throws Exception { 495 builder.appendValue(MONTH_OF_YEAR).appendLiteral(':') 496 .padNext(5).optionalStart().appendValue(DAY_OF_MONTH).optionalEnd() 497 .appendLiteral(':').appendValue(YEAR); 498 assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2: 1:2013"); 499 assertEquals(builder.toFormatter().format(YearMonth.of(2013, 2)), "2: :2013"); 500 } 501 502 //----------------------------------------------------------------------- 503 //----------------------------------------------------------------------- 504 //----------------------------------------------------------------------- 505 @Test(expectedExceptions=IllegalStateException.class) 506 public void test_optionalEnd_noStart() throws Exception { 507 builder.optionalEnd(); 508 } 509 510 //----------------------------------------------------------------------- 511 //----------------------------------------------------------------------- 512 //----------------------------------------------------------------------- 513 @DataProvider(name="validPatterns") 514 Object[][] dataValid() { 515 return new Object[][] { 516 {"'a'"}, 517 {"''"}, 518 {"'!'"}, 519 {"!"}, 520 {"'#'"}, 521 522 {"'hello_people,][)('"}, 523 {"'hi'"}, 524 {"'yyyy'"}, 525 {"''''"}, 526 {"'o''clock'"}, 527 528 {"G"}, 529 {"GG"}, 530 {"GGG"}, 531 {"GGGG"}, 532 {"GGGGG"}, 533 534 {"y"}, 535 {"yy"}, 536 {"yyy"}, 537 {"yyyy"}, 538 {"yyyyy"}, 539 540 {"M"}, 541 {"MM"}, 542 {"MMM"}, 543 {"MMMM"}, 544 {"MMMMM"}, 545 546 {"L"}, 547 {"LL"}, 548 {"LLL"}, 549 {"LLLL"}, 550 {"LLLLL"}, 551 552 {"D"}, 553 {"DD"}, 554 {"DDD"}, 555 556 {"d"}, 557 {"dd"}, 558 559 {"F"}, 560 561 {"Q"}, 562 {"QQ"}, 563 {"QQQ"}, 564 {"QQQQ"}, 565 {"QQQQQ"}, 566 567 {"q"}, 568 {"qq"}, 569 {"qqq"}, 570 {"qqqq"}, 571 {"qqqqq"}, 572 573 {"E"}, 574 {"EE"}, 575 {"EEE"}, 576 {"EEEE"}, 577 {"EEEEE"}, 578 579 {"e"}, 580 {"ee"}, 581 {"eee"}, 582 {"eeee"}, 583 {"eeeee"}, 584 585 {"c"}, 586 {"ccc"}, 587 {"cccc"}, 588 {"ccccc"}, 589 590 {"a"}, 591 592 {"H"}, 593 {"HH"}, 594 595 {"K"}, 596 {"KK"}, 597 598 {"k"}, 599 {"kk"}, 600 601 {"h"}, 602 {"hh"}, 603 604 {"m"}, 605 {"mm"}, 606 607 {"s"}, 608 {"ss"}, 609 610 {"S"}, 611 {"SS"}, 612 {"SSS"}, 613 {"SSSSSSSSS"}, 614 615 {"A"}, 616 {"AA"}, 617 {"AAA"}, 618 619 {"n"}, 620 {"nn"}, 621 {"nnn"}, 622 623 {"N"}, 624 {"NN"}, 625 {"NNN"}, 626 627 {"z"}, 628 {"zz"}, 629 {"zzz"}, 630 {"zzzz"}, 631 632 {"VV"}, 633 634 {"Z"}, 635 {"ZZ"}, 636 {"ZZZ"}, 637 638 {"X"}, 639 {"XX"}, 640 {"XXX"}, 641 {"XXXX"}, 642 {"XXXXX"}, 643 644 {"x"}, 645 {"xx"}, 646 {"xxx"}, 647 {"xxxx"}, 648 {"xxxxx"}, 649 650 {"ppH"}, 651 {"pppDD"}, 652 653 {"yyyy[-MM[-dd"}, 654 {"yyyy[-MM[-dd]]"}, 655 {"yyyy[-MM[]-dd]"}, 656 657 {"yyyy-MM-dd'T'HH:mm:ss.SSS"}, 658 659 {"e"}, 660 {"w"}, 661 {"ww"}, 662 {"W"}, 663 {"W"}, 664 665 }; 666 } 667 668 @Test(dataProvider="validPatterns") 669 public void test_appendPattern_valid(String input) throws Exception { 670 builder.appendPattern(input); // test is for no error here 671 } 672 673 //----------------------------------------------------------------------- 674 @DataProvider(name="invalidPatterns") 675 Object[][] dataInvalid() { 676 return new Object[][] { 677 {"'"}, 678 {"'hello"}, 679 {"'hel''lo"}, 680 {"'hello''"}, 681 {"{"}, 682 {"}"}, 683 {"{}"}, 684 {"#"}, 685 {"]"}, 686 {"yyyy]"}, 687 {"yyyy]MM"}, 688 {"yyyy[MM]]"}, 689 690 {"aa"}, 691 {"aaa"}, 692 {"aaaa"}, 693 {"aaaaa"}, 694 {"aaaaaa"}, 695 {"MMMMMM"}, 696 {"QQQQQQ"}, 697 {"qqqqqq"}, 698 {"EEEEEE"}, 699 {"eeeeee"}, 700 {"cc"}, 701 {"cccccc"}, 702 {"ddd"}, 703 {"DDDD"}, 704 {"FF"}, 705 {"FFF"}, 706 {"hhh"}, 707 {"HHH"}, 708 {"kkk"}, 709 {"KKK"}, 710 {"mmm"}, 711 {"sss"}, 712 {"OO"}, 713 {"OOO"}, 714 {"OOOOO"}, 715 {"XXXXXX"}, 716 {"zzzzz"}, 717 {"ZZZZZZ"}, 718 719 {"RO"}, 720 721 {"p"}, 722 {"pp"}, 723 {"p:"}, 724 725 {"f"}, 726 {"ff"}, 727 {"f:"}, 728 {"fy"}, 729 {"fa"}, 730 {"fM"}, 731 732 {"www"}, 733 {"WW"}, 734 }; 735 } 736 737 @Test(dataProvider="invalidPatterns", expectedExceptions=IllegalArgumentException.class) 738 public void test_appendPattern_invalid(String input) throws Exception { 739 builder.appendPattern(input); // test is for error here 740 } 741 742 //----------------------------------------------------------------------- 743 @DataProvider(name="patternPrint") 744 Object[][] data_patternPrint() { 745 return new Object[][] { 746 {"Q", date(2012, 2, 10), "1"}, 747 {"QQ", date(2012, 2, 10), "01"}, 748 {"QQQ", date(2012, 2, 10), "Q1"}, 749 {"QQQQ", date(2012, 2, 10), "1st quarter"}, 750 {"QQQQQ", date(2012, 2, 10), "1"}, 751 }; 752 } 753 754 @Test(dataProvider="patternPrint") 755 public void test_appendPattern_patternPrint(String input, Temporal temporal, String expected) throws Exception { 756 DateTimeFormatter f = builder.appendPattern(input).toFormatter(Locale.UK); 757 String test = f.format(temporal); 758 assertEquals(test, expected); 759 } 760 761 private static Temporal date(int y, int m, int d) { 762 return LocalDate.of(y, m, d); 763 } 764 765 //----------------------------------------------------------------------- 766 @Test 767 public void test_adjacent_strict_firstFixedWidth() throws Exception { 768 // succeeds because both number elements are fixed width 769 DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK); 770 ParsePosition pp = new ParsePosition(0); 771 TemporalAccessor parsed = f.parseUnresolved("12309", pp); 772 assertEquals(pp.getErrorIndex(), -1); 773 assertEquals(pp.getIndex(), 5); 774 assertEquals(parsed.getLong(HOUR_OF_DAY), 12L); 775 assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L); 776 } 777 778 @Test 779 public void test_adjacent_strict_firstVariableWidth_success() throws Exception { 780 // succeeds greedily parsing variable width, then fixed width, to non-numeric Z 781 DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('Z').toFormatter(Locale.UK); 782 ParsePosition pp = new ParsePosition(0); 783 TemporalAccessor parsed = f.parseUnresolved("12309Z", pp); 784 assertEquals(pp.getErrorIndex(), -1); 785 assertEquals(pp.getIndex(), 6); 786 assertEquals(parsed.getLong(HOUR_OF_DAY), 123L); 787 assertEquals(parsed.getLong(MINUTE_OF_HOUR), 9L); 788 } 789 790 @Test 791 public void test_adjacent_strict_firstVariableWidth_fails() throws Exception { 792 // fails because literal is a number and variable width parse greedily absorbs it 793 DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK); 794 ParsePosition pp = new ParsePosition(0); 795 TemporalAccessor parsed = f.parseUnresolved("12309", pp); 796 assertEquals(pp.getErrorIndex(), 5); 797 assertEquals(parsed, null); 798 } 799 800 @Test 801 public void test_adjacent_lenient() throws Exception { 802 // succeeds because both number elements are fixed width even in lenient mode 803 DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK); 804 ParsePosition pp = new ParsePosition(0); 805 TemporalAccessor parsed = f.parseUnresolved("12309", pp); 806 assertEquals(pp.getErrorIndex(), -1); 807 assertEquals(pp.getIndex(), 5); 808 assertEquals(parsed.getLong(HOUR_OF_DAY), 12L); 809 assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L); 810 } 811 812 @Test 813 public void test_adjacent_lenient_firstVariableWidth_success() throws Exception { 814 // succeeds greedily parsing variable width, then fixed width, to non-numeric Z 815 DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('Z').toFormatter(Locale.UK); 816 ParsePosition pp = new ParsePosition(0); 817 TemporalAccessor parsed = f.parseUnresolved("12309Z", pp); 818 assertEquals(pp.getErrorIndex(), -1); 819 assertEquals(pp.getIndex(), 6); 820 assertEquals(parsed.getLong(HOUR_OF_DAY), 123L); 821 assertEquals(parsed.getLong(MINUTE_OF_HOUR), 9L); 822 } 823 824 @Test 825 public void test_adjacent_lenient_firstVariableWidth_fails() throws Exception { 826 // fails because literal is a number and variable width parse greedily absorbs it 827 DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY).appendValue(MINUTE_OF_HOUR, 2).appendLiteral('9').toFormatter(Locale.UK); 828 ParsePosition pp = new ParsePosition(0); 829 TemporalAccessor parsed = f.parseUnresolved("12309", pp); 830 assertEquals(pp.getErrorIndex(), 5); 831 assertEquals(parsed, null); 832 } 833 834 //----------------------------------------------------------------------- 835 @Test 836 public void test_adjacent_strict_fractionFollows() throws Exception { 837 // succeeds because hour/min are fixed width 838 DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 0, 3, false).toFormatter(Locale.UK); 839 ParsePosition pp = new ParsePosition(0); 840 TemporalAccessor parsed = f.parseUnresolved("1230567", pp); 841 assertEquals(pp.getErrorIndex(), -1); 842 assertEquals(pp.getIndex(), 7); 843 assertEquals(parsed.getLong(HOUR_OF_DAY), 12L); 844 assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L); 845 assertEquals(parsed.getLong(NANO_OF_SECOND), 567_000_000L); 846 } 847 848 @Test 849 public void test_adjacent_strict_fractionFollows_2digit() throws Exception { 850 // succeeds because hour/min are fixed width 851 DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 0, 3, false).toFormatter(Locale.UK); 852 ParsePosition pp = new ParsePosition(0); 853 TemporalAccessor parsed = f.parseUnresolved("123056", pp); 854 assertEquals(pp.getErrorIndex(), -1); 855 assertEquals(pp.getIndex(), 6); 856 assertEquals(parsed.getLong(HOUR_OF_DAY), 12L); 857 assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L); 858 assertEquals(parsed.getLong(NANO_OF_SECOND), 560_000_000L); 859 } 860 861 @Test 862 public void test_adjacent_strict_fractionFollows_0digit() throws Exception { 863 // succeeds because hour/min are fixed width 864 DateTimeFormatter f = builder.appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 0, 3, false).toFormatter(Locale.UK); 865 ParsePosition pp = new ParsePosition(0); 866 TemporalAccessor parsed = f.parseUnresolved("1230", pp); 867 assertEquals(pp.getErrorIndex(), -1); 868 assertEquals(pp.getIndex(), 4); 869 assertEquals(parsed.getLong(HOUR_OF_DAY), 12L); 870 assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L); 871 } 872 873 @Test 874 public void test_adjacent_lenient_fractionFollows() throws Exception { 875 // succeeds because hour/min are fixed width 876 DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 3, 3, false).toFormatter(Locale.UK); 877 ParsePosition pp = new ParsePosition(0); 878 TemporalAccessor parsed = f.parseUnresolved("1230567", pp); 879 assertEquals(pp.getErrorIndex(), -1); 880 assertEquals(pp.getIndex(), 7); 881 assertEquals(parsed.getLong(HOUR_OF_DAY), 12L); 882 assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L); 883 assertEquals(parsed.getLong(NANO_OF_SECOND), 567_000_000L); 884 } 885 886 @Test(expectedExceptions=NullPointerException.class) 887 public void test_adjacent_lenient_fractionFollows_2digit() throws Exception { 888 // fails because hour,min and fraction of seconds are fixed width 889 DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 3, 3, false).toFormatter(Locale.UK); 890 ParsePosition pp = new ParsePosition(0); 891 TemporalAccessor parsed = f.parseUnresolved("123056", pp); 892 assertEquals(parsed.getLong(HOUR_OF_DAY), 12L); 893 assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L); 894 assertEquals(parsed.getLong(NANO_OF_SECOND), 560_000_000L); // fails here 895 } 896 897 @Test(expectedExceptions=NullPointerException.class) 898 public void test_adjacent_lenient_fractionFollows_0digit() throws Exception { 899 // fails because hour, min and fraction of seconds are fixed width 900 DateTimeFormatter f = builder.parseLenient().appendValue(HOUR_OF_DAY, 2).appendValue(MINUTE_OF_HOUR, 2).appendFraction(NANO_OF_SECOND, 3, 3, false).toFormatter(Locale.UK); 901 ParsePosition pp = new ParsePosition(0); 902 TemporalAccessor parsed = f.parseUnresolved("1230", pp); 903 assertEquals(parsed.getLong(HOUR_OF_DAY), 12L); 904 assertEquals(parsed.getLong(MINUTE_OF_HOUR), 30L); 905 assertEquals(parsed.getLong(NANO_OF_SECOND), 0L); // fails here 906 } 907 908 @DataProvider(name="adjacentFractionParseData") 909 Object[][] data_adjacent_fraction_parse() { 910 return new Object[][] { 911 {"20130812214600025", "yyyyMMddHHmmssSSS", LocalDateTime.of(2013, 8, 12, 21, 46, 00, 25000000)}, 912 {"201308122146000256", "yyyyMMddHHmmssSSSS", LocalDateTime.of(2013, 8, 12, 21, 46, 00, 25600000)}, 913 }; 914 } 915 916 @Test(dataProvider = "adjacentFractionParseData") 917 public void test_adjacent_fraction(String input, String pattern, LocalDateTime expected) { 918 DateTimeFormatter dtf = DateTimeFormatter.ofPattern(pattern); 919 LocalDateTime actual = LocalDateTime.parse(input, dtf); 920 assertEquals(actual, expected); 921 } 922 923 @DataProvider(name="lenientOffsetParseData") 924 Object[][] data_lenient_offset_parse() { 925 return new Object[][] { 926 {"+HH", "+01", 3600}, 927 {"+HH", "+0101", 3660}, 928 {"+HH", "+010101", 3661}, 929 {"+HH", "+01", 3600}, 930 {"+HH", "+01:01", 3660}, 931 {"+HH", "+01:01:01", 3661}, 932 {"+HHmm", "+01", 3600}, 933 {"+HHmm", "+0101", 3660}, 934 {"+HHmm", "+010101", 3661}, 935 {"+HH:mm", "+01", 3600}, 936 {"+HH:mm", "+01:01", 3660}, 937 {"+HH:mm", "+01:01:01", 3661}, 938 {"+HHMM", "+01", 3600}, 939 {"+HHMM", "+0101", 3660}, 940 {"+HHMM", "+010101", 3661}, 941 {"+HH:MM", "+01", 3600}, 942 {"+HH:MM", "+01:01", 3660}, 943 {"+HH:MM", "+01:01:01", 3661}, 944 {"+HHMMss", "+01", 3600}, 945 {"+HHMMss", "+0101", 3660}, 946 {"+HHMMss", "+010101", 3661}, 947 {"+HH:MM:ss", "+01", 3600}, 948 {"+HH:MM:ss", "+01:01", 3660}, 949 {"+HH:MM:ss", "+01:01:01", 3661}, 950 {"+HHMMSS", "+01", 3600}, 951 {"+HHMMSS", "+0101", 3660}, 952 {"+HHMMSS", "+010101", 3661}, 953 {"+HH:MM:SS", "+01", 3600}, 954 {"+HH:MM:SS", "+01:01", 3660}, 955 {"+HH:MM:SS", "+01:01:01", 3661}, 956 {"+HHmmss", "+01", 3600}, 957 {"+HHmmss", "+0101", 3660}, 958 {"+HHmmss", "+010101", 3661}, 959 {"+HH:mm:ss", "+01", 3600}, 960 {"+HH:mm:ss", "+01:01", 3660}, 961 {"+HH:mm:ss", "+01:01:01", 3661}, 962 }; 963 } 964 965 @Test(dataProvider="lenientOffsetParseData") 966 public void test_lenient_offset_parse_1(String pattern, String offset, int offsetSeconds) { 967 assertEquals(new DateTimeFormatterBuilder().parseLenient().appendOffset(pattern, "Z").toFormatter().parse(offset).get(OFFSET_SECONDS), 968 offsetSeconds); 969 } 970 971 @Test 972 public void test_lenient_offset_parse_2() { 973 assertEquals(new DateTimeFormatterBuilder().parseLenient().appendOffsetId().toFormatter().parse("+01").get(OFFSET_SECONDS), 974 3600); 975 } 976 977 @Test(expectedExceptions=DateTimeParseException.class) 978 public void test_strict_appendOffsetId() { 979 assertEquals(new DateTimeFormatterBuilder().appendOffsetId().toFormatter().parse("+01").get(OFFSET_SECONDS), 980 3600); 981 } 982 983 @Test(expectedExceptions=DateTimeParseException.class) 984 public void test_strict_appendOffset_1() { 985 assertEquals(new DateTimeFormatterBuilder().appendOffset("+HH:MM:ss", "Z").toFormatter().parse("+01").get(OFFSET_SECONDS), 986 3600); 987 } 988 989 @Test(expectedExceptions=DateTimeParseException.class) 990 public void test_strict_appendOffset_2() { 991 assertEquals(new DateTimeFormatterBuilder().appendOffset("+HHMMss", "Z").toFormatter().parse("+01").get(OFFSET_SECONDS), 992 3600); 993 } 994 995 @Test 996 public void test_basic_iso_date() { 997 assertEquals(BASIC_ISO_DATE.parse("20021231+01").get(OFFSET_SECONDS), 3600); 998 assertEquals(BASIC_ISO_DATE.parse("20021231+0101").get(OFFSET_SECONDS), 3660); 999 } 1000 1001 }