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