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