1 /* 2 * Copyright (c) 2012, 2020, 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.format.DateTimeFormatter.ISO_LOCAL_TIME; 63 import static java.time.temporal.ChronoField.DAY_OF_MONTH; 64 import static java.time.temporal.ChronoField.DAY_OF_WEEK; 65 import static java.time.temporal.ChronoField.HOUR_OF_DAY; 66 import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; 67 import static java.time.temporal.ChronoField.MONTH_OF_YEAR; 68 import static java.time.temporal.ChronoField.YEAR; 69 import static org.testng.Assert.assertEquals; 70 import static org.testng.Assert.assertNotNull; 71 import static org.testng.Assert.fail; 72 73 import android.icu.util.VersionInfo; 74 import java.text.ParsePosition; 75 import java.time.LocalDate; 76 import java.time.LocalTime; 77 import java.time.Period; 78 import java.time.YearMonth; 79 import java.time.ZoneOffset; 80 import java.time.chrono.Chronology; 81 import java.time.chrono.IsoChronology; 82 import java.time.format.DateTimeFormatter; 83 import java.time.format.DateTimeFormatterBuilder; 84 import java.time.format.DateTimeParseException; 85 import java.time.format.FormatStyle; 86 import java.time.format.ResolverStyle; 87 import java.time.format.SignStyle; 88 import java.time.format.TextStyle; 89 import java.time.temporal.ChronoField; 90 import java.time.temporal.Temporal; 91 import java.time.temporal.TemporalAccessor; 92 import java.time.temporal.TemporalField; 93 import java.time.temporal.TemporalUnit; 94 import java.time.temporal.ValueRange; 95 import java.util.HashMap; 96 import java.util.Locale; 97 import java.util.Map; 98 99 import org.testng.annotations.BeforeMethod; 100 import org.testng.annotations.DataProvider; 101 import org.testng.annotations.Test; 102 103 /** 104 * Test DateTimeFormatterBuilder. 105 */ 106 @Test 107 public class TestDateTimeFormatterBuilder { 108 109 private DateTimeFormatterBuilder builder; 110 111 @BeforeMethod setUp()112 public void setUp() { 113 builder = new DateTimeFormatterBuilder(); 114 } 115 116 //----------------------------------------------------------------------- 117 @Test test_toFormatter_empty()118 public void test_toFormatter_empty() throws Exception { 119 DateTimeFormatter f = builder.toFormatter(); 120 assertEquals(f.toString(), ""); 121 } 122 123 //----------------------------------------------------------------------- 124 @Test test_parseCaseSensitive()125 public void test_parseCaseSensitive() throws Exception { 126 builder.parseCaseSensitive(); 127 DateTimeFormatter f = builder.toFormatter(); 128 assertEquals(f.toString(), "ParseCaseSensitive(true)"); 129 } 130 131 @Test test_parseCaseInsensitive()132 public void test_parseCaseInsensitive() throws Exception { 133 builder.parseCaseInsensitive(); 134 DateTimeFormatter f = builder.toFormatter(); 135 assertEquals(f.toString(), "ParseCaseSensitive(false)"); 136 } 137 138 //----------------------------------------------------------------------- 139 @Test test_parseStrict()140 public void test_parseStrict() throws Exception { 141 builder.parseStrict(); 142 DateTimeFormatter f = builder.toFormatter(); 143 assertEquals(f.toString(), "ParseStrict(true)"); 144 } 145 146 @Test test_parseLenient()147 public void test_parseLenient() throws Exception { 148 builder.parseLenient(); 149 DateTimeFormatter f = builder.toFormatter(); 150 assertEquals(f.toString(), "ParseStrict(false)"); 151 } 152 153 //----------------------------------------------------------------------- 154 @Test test_appendValue_1arg()155 public void test_appendValue_1arg() throws Exception { 156 builder.appendValue(DAY_OF_MONTH); 157 DateTimeFormatter f = builder.toFormatter(); 158 assertEquals(f.toString(), "Value(DayOfMonth)"); 159 } 160 161 @Test(expectedExceptions=NullPointerException.class) test_appendValue_1arg_null()162 public void test_appendValue_1arg_null() throws Exception { 163 builder.appendValue(null); 164 } 165 166 //----------------------------------------------------------------------- 167 @Test test_appendValue_2arg()168 public void test_appendValue_2arg() throws Exception { 169 builder.appendValue(DAY_OF_MONTH, 3); 170 DateTimeFormatter f = builder.toFormatter(); 171 assertEquals(f.toString(), "Value(DayOfMonth,3)"); 172 } 173 174 @Test(expectedExceptions=NullPointerException.class) test_appendValue_2arg_null()175 public void test_appendValue_2arg_null() throws Exception { 176 builder.appendValue(null, 3); 177 } 178 179 @Test(expectedExceptions=IllegalArgumentException.class) test_appendValue_2arg_widthTooSmall()180 public void test_appendValue_2arg_widthTooSmall() throws Exception { 181 builder.appendValue(DAY_OF_MONTH, 0); 182 } 183 184 @Test(expectedExceptions=IllegalArgumentException.class) test_appendValue_2arg_widthTooBig()185 public void test_appendValue_2arg_widthTooBig() throws Exception { 186 builder.appendValue(DAY_OF_MONTH, 20); 187 } 188 189 //----------------------------------------------------------------------- 190 @Test test_appendValue_3arg()191 public void test_appendValue_3arg() throws Exception { 192 builder.appendValue(DAY_OF_MONTH, 2, 3, SignStyle.NORMAL); 193 DateTimeFormatter f = builder.toFormatter(); 194 assertEquals(f.toString(), "Value(DayOfMonth,2,3,NORMAL)"); 195 } 196 197 @Test(expectedExceptions=NullPointerException.class) test_appendValue_3arg_nullField()198 public void test_appendValue_3arg_nullField() throws Exception { 199 builder.appendValue(null, 2, 3, SignStyle.NORMAL); 200 } 201 202 @Test(expectedExceptions=IllegalArgumentException.class) test_appendValue_3arg_minWidthTooSmall()203 public void test_appendValue_3arg_minWidthTooSmall() throws Exception { 204 builder.appendValue(DAY_OF_MONTH, 0, 2, SignStyle.NORMAL); 205 } 206 207 @Test(expectedExceptions=IllegalArgumentException.class) test_appendValue_3arg_minWidthTooBig()208 public void test_appendValue_3arg_minWidthTooBig() throws Exception { 209 builder.appendValue(DAY_OF_MONTH, 20, 2, SignStyle.NORMAL); 210 } 211 212 @Test(expectedExceptions=IllegalArgumentException.class) test_appendValue_3arg_maxWidthTooSmall()213 public void test_appendValue_3arg_maxWidthTooSmall() throws Exception { 214 builder.appendValue(DAY_OF_MONTH, 2, 0, SignStyle.NORMAL); 215 } 216 217 @Test(expectedExceptions=IllegalArgumentException.class) test_appendValue_3arg_maxWidthTooBig()218 public void test_appendValue_3arg_maxWidthTooBig() throws Exception { 219 builder.appendValue(DAY_OF_MONTH, 2, 20, SignStyle.NORMAL); 220 } 221 222 @Test(expectedExceptions=IllegalArgumentException.class) test_appendValue_3arg_maxWidthMinWidth()223 public void test_appendValue_3arg_maxWidthMinWidth() throws Exception { 224 builder.appendValue(DAY_OF_MONTH, 4, 2, SignStyle.NORMAL); 225 } 226 227 @Test(expectedExceptions=NullPointerException.class) test_appendValue_3arg_nullSignStyle()228 public void test_appendValue_3arg_nullSignStyle() throws Exception { 229 builder.appendValue(DAY_OF_MONTH, 2, 3, null); 230 } 231 232 //----------------------------------------------------------------------- 233 @Test test_appendValue_subsequent2_parse3()234 public void test_appendValue_subsequent2_parse3() throws Exception { 235 builder.appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL).appendValue(DAY_OF_MONTH, 2); 236 DateTimeFormatter f = builder.toFormatter(); 237 assertEquals(f.toString(), "Value(MonthOfYear,1,2,NORMAL)Value(DayOfMonth,2)"); 238 TemporalAccessor parsed = f.parseUnresolved("123", new ParsePosition(0)); 239 assertEquals(parsed.getLong(MONTH_OF_YEAR), 1L); 240 assertEquals(parsed.getLong(DAY_OF_MONTH), 23L); 241 } 242 243 @Test test_appendValue_subsequent2_parse4()244 public void test_appendValue_subsequent2_parse4() throws Exception { 245 builder.appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL).appendValue(DAY_OF_MONTH, 2); 246 DateTimeFormatter f = builder.toFormatter(); 247 assertEquals(f.toString(), "Value(MonthOfYear,1,2,NORMAL)Value(DayOfMonth,2)"); 248 TemporalAccessor parsed = f.parseUnresolved("0123", new ParsePosition(0)); 249 assertEquals(parsed.getLong(MONTH_OF_YEAR), 1L); 250 assertEquals(parsed.getLong(DAY_OF_MONTH), 23L); 251 } 252 253 @Test test_appendValue_subsequent2_parse5()254 public void test_appendValue_subsequent2_parse5() throws Exception { 255 builder.appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL).appendValue(DAY_OF_MONTH, 2).appendLiteral('4'); 256 DateTimeFormatter f = builder.toFormatter(); 257 assertEquals(f.toString(), "Value(MonthOfYear,1,2,NORMAL)Value(DayOfMonth,2)'4'"); 258 TemporalAccessor parsed = f.parseUnresolved("01234", new ParsePosition(0)); 259 assertEquals(parsed.getLong(MONTH_OF_YEAR), 1L); 260 assertEquals(parsed.getLong(DAY_OF_MONTH), 23L); 261 } 262 263 @Test test_appendValue_subsequent3_parse6()264 public void test_appendValue_subsequent3_parse6() throws Exception { 265 builder 266 .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD) 267 .appendValue(MONTH_OF_YEAR, 2) 268 .appendValue(DAY_OF_MONTH, 2); 269 DateTimeFormatter f = builder.toFormatter(); 270 assertEquals(f.toString(), "Value(Year,4,10,EXCEEDS_PAD)Value(MonthOfYear,2)Value(DayOfMonth,2)"); 271 TemporalAccessor parsed = f.parseUnresolved("20090630", new ParsePosition(0)); 272 assertEquals(parsed.getLong(YEAR), 2009L); 273 assertEquals(parsed.getLong(MONTH_OF_YEAR), 6L); 274 assertEquals(parsed.getLong(DAY_OF_MONTH), 30L); 275 } 276 277 //----------------------------------------------------------------------- 278 @Test(expectedExceptions=NullPointerException.class) test_appendValueReduced_null()279 public void test_appendValueReduced_null() throws Exception { 280 builder.appendValueReduced(null, 2, 2, 2000); 281 } 282 283 @Test test_appendValueReduced()284 public void test_appendValueReduced() throws Exception { 285 builder.appendValueReduced(YEAR, 2, 2, 2000); 286 DateTimeFormatter f = builder.toFormatter(); 287 assertEquals(f.toString(), "ReducedValue(Year,2,2,2000)"); 288 TemporalAccessor parsed = f.parseUnresolved("12", new ParsePosition(0)); 289 assertEquals(parsed.getLong(YEAR), 2012L); 290 } 291 292 @Test test_appendValueReduced_subsequent_parse()293 public void test_appendValueReduced_subsequent_parse() throws Exception { 294 builder.appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL).appendValueReduced(YEAR, 2, 2, 2000); 295 DateTimeFormatter f = builder.toFormatter(); 296 assertEquals(f.toString(), "Value(MonthOfYear,1,2,NORMAL)ReducedValue(Year,2,2,2000)"); 297 ParsePosition ppos = new ParsePosition(0); 298 TemporalAccessor parsed = f.parseUnresolved("123", ppos); 299 assertNotNull(parsed, "Parse failed: " + ppos.toString()); 300 assertEquals(parsed.getLong(MONTH_OF_YEAR), 1L); 301 assertEquals(parsed.getLong(YEAR), 2023L); 302 } 303 304 //----------------------------------------------------------------------- 305 //----------------------------------------------------------------------- 306 //----------------------------------------------------------------------- 307 @Test test_appendFraction_4arg()308 public void test_appendFraction_4arg() throws Exception { 309 builder.appendFraction(MINUTE_OF_HOUR, 1, 9, false); 310 DateTimeFormatter f = builder.toFormatter(); 311 assertEquals(f.toString(), "Fraction(MinuteOfHour,1,9)"); 312 } 313 314 @Test(expectedExceptions=NullPointerException.class) test_appendFraction_4arg_nullRule()315 public void test_appendFraction_4arg_nullRule() throws Exception { 316 builder.appendFraction(null, 1, 9, false); 317 } 318 319 @Test(expectedExceptions=IllegalArgumentException.class) test_appendFraction_4arg_invalidRuleNotFixedSet()320 public void test_appendFraction_4arg_invalidRuleNotFixedSet() throws Exception { 321 builder.appendFraction(DAY_OF_MONTH, 1, 9, false); 322 } 323 324 @Test(expectedExceptions=IllegalArgumentException.class) test_appendFraction_4arg_minTooSmall()325 public void test_appendFraction_4arg_minTooSmall() throws Exception { 326 builder.appendFraction(MINUTE_OF_HOUR, -1, 9, false); 327 } 328 329 @Test(expectedExceptions=IllegalArgumentException.class) test_appendFraction_4arg_minTooBig()330 public void test_appendFraction_4arg_minTooBig() throws Exception { 331 builder.appendFraction(MINUTE_OF_HOUR, 10, 9, false); 332 } 333 334 @Test(expectedExceptions=IllegalArgumentException.class) test_appendFraction_4arg_maxTooSmall()335 public void test_appendFraction_4arg_maxTooSmall() throws Exception { 336 builder.appendFraction(MINUTE_OF_HOUR, 0, -1, false); 337 } 338 339 @Test(expectedExceptions=IllegalArgumentException.class) test_appendFraction_4arg_maxTooBig()340 public void test_appendFraction_4arg_maxTooBig() throws Exception { 341 builder.appendFraction(MINUTE_OF_HOUR, 1, 10, false); 342 } 343 344 @Test(expectedExceptions=IllegalArgumentException.class) test_appendFraction_4arg_maxWidthMinWidth()345 public void test_appendFraction_4arg_maxWidthMinWidth() throws Exception { 346 builder.appendFraction(MINUTE_OF_HOUR, 9, 3, false); 347 } 348 349 //----------------------------------------------------------------------- 350 //----------------------------------------------------------------------- 351 //----------------------------------------------------------------------- 352 @Test test_appendText_1arg()353 public void test_appendText_1arg() throws Exception { 354 builder.appendText(MONTH_OF_YEAR); 355 DateTimeFormatter f = builder.toFormatter(); 356 assertEquals(f.toString(), "Text(MonthOfYear)"); 357 } 358 359 @Test(expectedExceptions=NullPointerException.class) test_appendText_1arg_null()360 public void test_appendText_1arg_null() throws Exception { 361 builder.appendText(null); 362 } 363 364 //----------------------------------------------------------------------- 365 @Test test_appendText_2arg()366 public void test_appendText_2arg() throws Exception { 367 builder.appendText(MONTH_OF_YEAR, TextStyle.SHORT); 368 DateTimeFormatter f = builder.toFormatter(); 369 assertEquals(f.toString(), "Text(MonthOfYear,SHORT)"); 370 } 371 372 @Test(expectedExceptions=NullPointerException.class) test_appendText_2arg_nullRule()373 public void test_appendText_2arg_nullRule() throws Exception { 374 builder.appendText(null, TextStyle.SHORT); 375 } 376 377 @Test(expectedExceptions=NullPointerException.class) test_appendText_2arg_nullStyle()378 public void test_appendText_2arg_nullStyle() throws Exception { 379 builder.appendText(MONTH_OF_YEAR, (TextStyle) null); 380 } 381 382 //----------------------------------------------------------------------- 383 @Test test_appendTextMap()384 public void test_appendTextMap() throws Exception { 385 Map<Long, String> map = new HashMap<>(); 386 map.put(1L, "JNY"); 387 map.put(2L, "FBY"); 388 map.put(3L, "MCH"); 389 map.put(4L, "APL"); 390 map.put(5L, "MAY"); 391 map.put(6L, "JUN"); 392 map.put(7L, "JLY"); 393 map.put(8L, "AGT"); 394 map.put(9L, "SPT"); 395 map.put(10L, "OBR"); 396 map.put(11L, "NVR"); 397 map.put(12L, "DBR"); 398 builder.appendText(MONTH_OF_YEAR, map); 399 DateTimeFormatter f = builder.toFormatter(); 400 assertEquals(f.toString(), "Text(MonthOfYear)"); // TODO: toString should be different? 401 } 402 403 @Test(expectedExceptions=NullPointerException.class) test_appendTextMap_nullRule()404 public void test_appendTextMap_nullRule() throws Exception { 405 builder.appendText(null, new HashMap<Long, String>()); 406 } 407 408 @Test(expectedExceptions=NullPointerException.class) test_appendTextMap_nullStyle()409 public void test_appendTextMap_nullStyle() throws Exception { 410 builder.appendText(MONTH_OF_YEAR, (Map<Long, String>) null); 411 } 412 413 //----------------------------------------------------------------------- 414 //----------------------------------------------------------------------- 415 //----------------------------------------------------------------------- 416 @Test test_appendOffsetId()417 public void test_appendOffsetId() throws Exception { 418 builder.appendOffsetId(); 419 DateTimeFormatter f = builder.toFormatter(); 420 assertEquals(f.toString(), "Offset(+HH:MM:ss,'Z')"); 421 } 422 423 @DataProvider(name="offsetPatterns") data_offsetPatterns()424 Object[][] data_offsetPatterns() { 425 return new Object[][] { 426 {"+HH", 2, 0, 0, "+02"}, 427 {"+HH", -2, 0, 0, "-02"}, 428 {"+HH", 2, 30, 0, "+02"}, 429 {"+HH", 2, 0, 45, "+02"}, 430 {"+HH", 2, 30, 45, "+02"}, 431 432 {"+HHMM", 2, 0, 0, "+0200"}, 433 {"+HHMM", -2, 0, 0, "-0200"}, 434 {"+HHMM", 2, 30, 0, "+0230"}, 435 {"+HHMM", 2, 0, 45, "+0200"}, 436 {"+HHMM", 2, 30, 45, "+0230"}, 437 438 {"+HH:MM", 2, 0, 0, "+02:00"}, 439 {"+HH:MM", -2, 0, 0, "-02:00"}, 440 {"+HH:MM", 2, 30, 0, "+02:30"}, 441 {"+HH:MM", 2, 0, 45, "+02:00"}, 442 {"+HH:MM", 2, 30, 45, "+02:30"}, 443 444 {"+HHMMss", 2, 0, 0, "+0200"}, 445 {"+HHMMss", -2, 0, 0, "-0200"}, 446 {"+HHMMss", 2, 30, 0, "+0230"}, 447 {"+HHMMss", 2, 0, 45, "+020045"}, 448 {"+HHMMss", 2, 30, 45, "+023045"}, 449 450 {"+HH:MM:ss", 2, 0, 0, "+02:00"}, 451 {"+HH:MM:ss", -2, 0, 0, "-02:00"}, 452 {"+HH:MM:ss", 2, 30, 0, "+02:30"}, 453 {"+HH:MM:ss", 2, 0, 45, "+02:00:45"}, 454 {"+HH:MM:ss", 2, 30, 45, "+02:30:45"}, 455 456 {"+HHMMSS", 2, 0, 0, "+020000"}, 457 {"+HHMMSS", -2, 0, 0, "-020000"}, 458 {"+HHMMSS", 2, 30, 0, "+023000"}, 459 {"+HHMMSS", 2, 0, 45, "+020045"}, 460 {"+HHMMSS", 2, 30, 45, "+023045"}, 461 462 {"+HH:MM:SS", 2, 0, 0, "+02:00:00"}, 463 {"+HH:MM:SS", -2, 0, 0, "-02:00:00"}, 464 {"+HH:MM:SS", 2, 30, 0, "+02:30:00"}, 465 {"+HH:MM:SS", 2, 0, 45, "+02:00:45"}, 466 {"+HH:MM:SS", 2, 30, 45, "+02:30:45"}, 467 }; 468 } 469 470 @Test(dataProvider="offsetPatterns") test_appendOffset_format(String pattern, int h, int m, int s, String expected)471 public void test_appendOffset_format(String pattern, int h, int m, int s, String expected) throws Exception { 472 builder.appendOffset(pattern, "Z"); 473 DateTimeFormatter f = builder.toFormatter(); 474 ZoneOffset offset = ZoneOffset.ofHoursMinutesSeconds(h, m, s); 475 assertEquals(f.format(offset), expected); 476 } 477 478 @Test(dataProvider="offsetPatterns") test_appendOffset_parse(String pattern, int h, int m, int s, String expected)479 public void test_appendOffset_parse(String pattern, int h, int m, int s, String expected) throws Exception { 480 builder.appendOffset(pattern, "Z"); 481 DateTimeFormatter f = builder.toFormatter(); 482 ZoneOffset offset = ZoneOffset.ofHoursMinutesSeconds(h, m, s); 483 ZoneOffset parsed = f.parse(expected, ZoneOffset::from); 484 assertEquals(f.format(parsed), expected); 485 } 486 487 @DataProvider(name="badOffsetPatterns") data_badOffsetPatterns()488 Object[][] data_badOffsetPatterns() { 489 return new Object[][] { 490 {"HH"}, 491 {"HHMM"}, 492 {"HH:MM"}, 493 {"HHMMss"}, 494 {"HH:MM:ss"}, 495 {"HHMMSS"}, 496 {"HH:MM:SS"}, 497 {"+HHM"}, 498 {"+A"}, 499 }; 500 } 501 502 @Test(dataProvider="badOffsetPatterns", expectedExceptions=IllegalArgumentException.class) test_appendOffset_badPattern(String pattern)503 public void test_appendOffset_badPattern(String pattern) throws Exception { 504 builder.appendOffset(pattern, "Z"); 505 } 506 507 @Test(expectedExceptions=NullPointerException.class) test_appendOffset_3arg_nullText()508 public void test_appendOffset_3arg_nullText() throws Exception { 509 builder.appendOffset("+HH:MM", null); 510 } 511 512 @Test(expectedExceptions=NullPointerException.class) test_appendOffset_3arg_nullPattern()513 public void test_appendOffset_3arg_nullPattern() throws Exception { 514 builder.appendOffset(null, "Z"); 515 } 516 517 //----------------------------------------------------------------------- 518 //----------------------------------------------------------------------- 519 //----------------------------------------------------------------------- 520 @Test test_appendZoneId()521 public void test_appendZoneId() throws Exception { 522 builder.appendZoneId(); 523 DateTimeFormatter f = builder.toFormatter(); 524 assertEquals(f.toString(), "ZoneId()"); 525 } 526 527 @Test test_appendZoneText_1arg()528 public void test_appendZoneText_1arg() throws Exception { 529 builder.appendZoneText(TextStyle.FULL); 530 DateTimeFormatter f = builder.toFormatter(); 531 assertEquals(f.toString(), "ZoneText(FULL)"); 532 } 533 534 @Test(expectedExceptions=NullPointerException.class) test_appendZoneText_1arg_nullText()535 public void test_appendZoneText_1arg_nullText() throws Exception { 536 builder.appendZoneText(null); 537 } 538 539 //----------------------------------------------------------------------- 540 //----------------------------------------------------------------------- 541 //----------------------------------------------------------------------- 542 // BEGIN Android-removed: DateTimeFormatBuilder.appendDayPeriodText is temporarily unsupported. 543 /* 544 @Test 545 public void test_appendDayPeriodText_1arg() throws Exception { 546 builder.appendDayPeriodText(TextStyle.FULL); 547 DateTimeFormatter f = builder.toFormatter(); 548 assertEquals(f.toString(), "DayPeriod(FULL)"); 549 } 550 551 @Test(expectedExceptions=NullPointerException.class) 552 public void test_appendDayPeriodText_1arg_nullText() throws Exception { 553 builder.appendDayPeriodText(null); 554 } 555 556 @DataProvider(name="dayPeriodFormat") 557 Object[][] data_dayPeriodFormat() { 558 return new Object[][] { 559 {0, 0, TextStyle.FULL, Locale.US, "midnight"}, 560 {0, 1, TextStyle.FULL, Locale.US, "at night"}, 561 {6, 0, TextStyle.FULL, Locale.US, "in the morning"}, 562 {12, 0, TextStyle.FULL, Locale.US, "noon"}, 563 {12, 1, TextStyle.FULL, Locale.US, "in the afternoon"}, 564 {18, 0, TextStyle.FULL, Locale.US, "in the evening"}, 565 {22, 0, TextStyle.FULL, Locale.US, "at night"}, 566 567 {0, 0, TextStyle.FULL, Locale.JAPAN, "\u771f\u591c\u4e2d"}, 568 {0, 1, TextStyle.FULL, Locale.JAPAN, "\u591c\u4e2d"}, 569 {6, 0, TextStyle.FULL, Locale.JAPAN, "\u671d"}, 570 {12, 0, TextStyle.FULL, Locale.JAPAN, "\u6b63\u5348"}, 571 {12, 1, TextStyle.FULL, Locale.JAPAN, "\u663c"}, 572 {18, 0, TextStyle.FULL, Locale.JAPAN, "\u5915\u65b9"}, 573 {19, 0, TextStyle.FULL, Locale.JAPAN, "\u591c"}, 574 {23, 0, TextStyle.FULL, Locale.JAPAN, "\u591c\u4e2d"}, 575 576 {0, 0, TextStyle.NARROW, Locale.US, "mi"}, 577 {0, 1, TextStyle.NARROW, Locale.US, "at night"}, 578 {6, 0, TextStyle.NARROW, Locale.US, "in the morning"}, 579 {12, 0, TextStyle.NARROW, Locale.US, "n"}, 580 {12, 1, TextStyle.NARROW, Locale.US, "in the afternoon"}, 581 {18, 0, TextStyle.NARROW, Locale.US, "in the evening"}, 582 {22, 0, TextStyle.NARROW, Locale.US, "at night"}, 583 584 {0, 0, TextStyle.NARROW, Locale.JAPAN, "\u771f\u591c\u4e2d"}, 585 {0, 1, TextStyle.NARROW, Locale.JAPAN, "\u591c\u4e2d"}, 586 {6, 0, TextStyle.NARROW, Locale.JAPAN, "\u671d"}, 587 {12, 0, TextStyle.NARROW, Locale.JAPAN, "\u6b63\u5348"}, 588 {12, 1, TextStyle.NARROW, Locale.JAPAN, "\u663c"}, 589 {18, 0, TextStyle.NARROW, Locale.JAPAN, "\u5915\u65b9"}, 590 {19, 0, TextStyle.NARROW, Locale.JAPAN, "\u591c"}, 591 {23, 0, TextStyle.NARROW, Locale.JAPAN, "\u591c\u4e2d"}, 592 }; 593 } 594 @Test (dataProvider="dayPeriodFormat") 595 public void test_dayPeriodFormat(int hod, int moh, TextStyle ts, Locale l, String expected) throws Exception { 596 builder.appendDayPeriodText(ts); 597 LocalTime t = LocalTime.of(hod, moh); 598 DateTimeFormatter f = builder.toFormatter().withLocale(l); 599 assertEquals(f.format(t), expected); 600 } 601 602 @DataProvider(name="dayPeriodParse") 603 Object[][] data_dayPeriodParse() { 604 return new Object[][] { 605 {TextStyle.FULL, Locale.US, 0, 0, "midnight"}, 606 {TextStyle.FULL, Locale.US, 1, 30, "at night"}, 607 {TextStyle.FULL, Locale.US, 6, 0, "AM"}, 608 {TextStyle.FULL, Locale.US, 9, 0, "in the morning"}, 609 {TextStyle.FULL, Locale.US, 12, 0, "noon"}, 610 {TextStyle.FULL, Locale.US, 15, 0, "in the afternoon"}, 611 {TextStyle.FULL, Locale.US, 18, 0, "PM"}, 612 {TextStyle.FULL, Locale.US, 19, 30, "in the evening"}, 613 614 {TextStyle.FULL, Locale.JAPAN, 0, 0, "\u771f\u591c\u4e2d"}, 615 {TextStyle.FULL, Locale.JAPAN, 1, 30, "\u591c\u4e2d"}, 616 {TextStyle.FULL, Locale.JAPAN, 6, 0, "\u5348\u524d"}, 617 {TextStyle.FULL, Locale.JAPAN, 8, 0, "\u671d"}, 618 {TextStyle.FULL, Locale.JAPAN, 12, 0, "\u6b63\u5348"}, 619 {TextStyle.FULL, Locale.JAPAN, 14, 0, "\u663c"}, 620 {TextStyle.FULL, Locale.JAPAN, 17, 30, "\u5915\u65b9"}, 621 {TextStyle.FULL, Locale.JAPAN, 18, 0, "\u5348\u5f8c"}, 622 {TextStyle.FULL, Locale.JAPAN, 21, 0, "\u591c"}, 623 624 {TextStyle.NARROW, Locale.US, 0, 0, "mi"}, 625 {TextStyle.NARROW, Locale.US, 1, 30, "at night"}, 626 {TextStyle.NARROW, Locale.US, 6, 0, "a"}, 627 {TextStyle.NARROW, Locale.US, 9, 0, "in the morning"}, 628 {TextStyle.NARROW, Locale.US, 12, 0, "n"}, 629 {TextStyle.NARROW, Locale.US, 15, 0, "in the afternoon"}, 630 {TextStyle.NARROW, Locale.US, 18, 0, "p"}, 631 {TextStyle.NARROW, Locale.US, 19, 30, "in the evening"}, 632 633 {TextStyle.NARROW, Locale.JAPAN, 0, 0, "\u771f\u591c\u4e2d"}, 634 {TextStyle.NARROW, Locale.JAPAN, 1, 30, "\u591c\u4e2d"}, 635 {TextStyle.NARROW, Locale.JAPAN, 6, 0, "\u5348\u524d"}, 636 {TextStyle.NARROW, Locale.JAPAN, 8, 0, "\u671d"}, 637 {TextStyle.NARROW, Locale.JAPAN, 12, 0, "\u6b63\u5348"}, 638 {TextStyle.NARROW, Locale.JAPAN, 14, 0, "\u663c"}, 639 {TextStyle.NARROW, Locale.JAPAN, 17, 30, "\u5915\u65b9"}, 640 {TextStyle.NARROW, Locale.JAPAN, 18, 0, "\u5348\u5f8c"}, 641 {TextStyle.NARROW, Locale.JAPAN, 21, 0, "\u591c"}, 642 }; 643 } 644 @Test (dataProvider="dayPeriodParse") 645 public void test_dayPeriodParse(TextStyle ts, Locale l, long hod, long moh, String dayPeriod) throws Exception { 646 builder.appendDayPeriodText(ts); 647 DateTimeFormatter f = builder.toFormatter().withLocale(l); 648 var p = f.parse(dayPeriod); 649 assertEquals(p.getLong(HOUR_OF_DAY), hod); 650 assertEquals(p.getLong(MINUTE_OF_HOUR), moh); 651 } 652 653 @DataProvider(name="dayPeriodParsePattern") 654 Object[][] data_dayPeriodParsePattern() { 655 return new Object[][] { 656 {"H B", "23 at night", 23}, 657 {"H B", "3 at night", 3}, 658 {"K B", "11 at night", 23}, 659 {"K B", "3 at night", 3}, 660 {"K B", "11 in the morning", 11}, 661 {"h B", "11 at night", 23}, 662 {"h B", "3 at night", 3}, 663 {"h B", "11 in the morning", 11}, 664 {"a", "AM", 6}, 665 {"a", "PM", 18}, 666 }; 667 } 668 669 @Test (dataProvider="dayPeriodParsePattern") 670 public void test_dayPeriodParsePattern(String pattern, String hourDayPeriod, long expected) throws Exception { 671 builder.appendPattern(pattern); 672 DateTimeFormatter f = builder.toFormatter().withLocale(Locale.US); 673 var p = f.parse(hourDayPeriod); 674 assertEquals(p.getLong(HOUR_OF_DAY), expected); 675 } 676 677 @DataProvider(name="dayPeriodParseMidnight") 678 Object[][] data_dayPeriodParseMidnight() { 679 return new Object[][] { 680 {"u-M-d H:m B", "2020-11-07 00:00 midnight", 7, 0}, 681 {"u-M-d H:m B", "2020-11-07 24:00 midnight", 8, 0}, 682 }; 683 } 684 685 @Test (dataProvider="dayPeriodParseMidnight") 686 public void test_dayPeriodParseMidnight(String pattern, String dateTime, long expectedDOM, long expectedHOD) throws Exception { 687 builder.appendPattern(pattern); 688 DateTimeFormatter f = builder.toFormatter().withLocale(Locale.US); 689 var p = f.parse(dateTime); 690 assertEquals(p.getLong(DAY_OF_MONTH), expectedDOM); 691 assertEquals(p.getLong(HOUR_OF_DAY), expectedHOD); 692 } 693 694 @DataProvider(name="dayPeriodParseInvalid") 695 Object[][] data_dayPeriodParseInvalid() { 696 return new Object[][] { 697 {TextStyle.FULL, ResolverStyle.SMART, Locale.US, "00:01 midnight", "00:00"}, 698 {TextStyle.FULL, ResolverStyle.SMART, Locale.US, "06:01 at night", "21:00-06:00"}, 699 {TextStyle.FULL, ResolverStyle.SMART, Locale.US, "05:59 in the morning", "06:00-12:00"}, 700 {TextStyle.FULL, ResolverStyle.SMART, Locale.US, "11:59 noon", "12:00"}, 701 {TextStyle.FULL, ResolverStyle.SMART, Locale.US, "18:00 in the afternoon", "12:00-18:00"}, 702 {TextStyle.FULL, ResolverStyle.SMART, Locale.US, "17:59 in the evening", "18:00-21:00"}, 703 {TextStyle.NARROW, ResolverStyle.SMART, Locale.US, "00:01 mi", "00:00"}, 704 {TextStyle.NARROW, ResolverStyle.SMART, Locale.US, "06:01 at night", "21:00-06:00"}, 705 {TextStyle.NARROW, ResolverStyle.SMART, Locale.US, "05:59 in the morning", "06:00-12:00"}, 706 {TextStyle.NARROW, ResolverStyle.SMART, Locale.US, "11:59 n", "12:00"}, 707 {TextStyle.NARROW, ResolverStyle.SMART, Locale.US, "18:00 in the afternoon", "12:00-18:00"}, 708 {TextStyle.NARROW, ResolverStyle.SMART, Locale.US, "17:59 in the evening", "18:00-21:00"}, 709 710 {TextStyle.FULL, ResolverStyle.SMART, Locale.JAPAN, "00:01 \u771f\u591c\u4e2d", "00:00"}, 711 {TextStyle.FULL, ResolverStyle.SMART, Locale.JAPAN, "04:00 \u591c\u4e2d", "23:00-04:00"}, 712 {TextStyle.FULL, ResolverStyle.SMART, Locale.JAPAN, "03:59 \u671d", "04:00-12:00"}, 713 {TextStyle.FULL, ResolverStyle.SMART, Locale.JAPAN, "12:01 \u6b63\u5348", "12:00"}, 714 {TextStyle.FULL, ResolverStyle.SMART, Locale.JAPAN, "16:00 \u663c", "12:00-16:00"}, 715 {TextStyle.FULL, ResolverStyle.SMART, Locale.JAPAN, "19:01 \u5915\u65b9", "16:00-19:00"}, 716 {TextStyle.FULL, ResolverStyle.SMART, Locale.JAPAN, "23:00 \u591c", "19:00-23:00"}, 717 {TextStyle.NARROW, ResolverStyle.SMART, Locale.JAPAN, "00:01 \u771f\u591c\u4e2d", "00:00"}, 718 {TextStyle.NARROW, ResolverStyle.SMART, Locale.JAPAN, "04:00 \u591c\u4e2d", "23:00-04:00"}, 719 {TextStyle.NARROW, ResolverStyle.SMART, Locale.JAPAN, "03:59 \u671d", "04:00-12:00"}, 720 {TextStyle.NARROW, ResolverStyle.SMART, Locale.JAPAN, "12:01 \u6b63\u5348", "12:00"}, 721 {TextStyle.NARROW, ResolverStyle.SMART, Locale.JAPAN, "16:00 \u663c", "12:00-16:00"}, 722 {TextStyle.NARROW, ResolverStyle.SMART, Locale.JAPAN, "19:01 \u5915\u65b9", "16:00-19:00"}, 723 {TextStyle.NARROW, ResolverStyle.SMART, Locale.JAPAN, "23:00 \u591c", "19:00-23:00"}, 724 725 {TextStyle.FULL, ResolverStyle.LENIENT, Locale.US, "00:01 midnight", "00:00"}, 726 {TextStyle.FULL, ResolverStyle.LENIENT, Locale.US, "06:01 at night", "21:00-06:00"}, 727 {TextStyle.FULL, ResolverStyle.LENIENT, Locale.US, "05:59 in the morning", "06:00-12:00"}, 728 {TextStyle.FULL, ResolverStyle.LENIENT, Locale.US, "11:59 noon", "12:00"}, 729 {TextStyle.FULL, ResolverStyle.LENIENT, Locale.US, "18:00 in the afternoon", "12:00-18:00"}, 730 {TextStyle.FULL, ResolverStyle.LENIENT, Locale.US, "17:59 in the evening", "18:00-21:00"}, 731 {TextStyle.NARROW, ResolverStyle.LENIENT, Locale.US, "00:01 mi", "00:00"}, 732 {TextStyle.NARROW, ResolverStyle.LENIENT, Locale.US, "06:01 at night", "21:00-06:00"}, 733 {TextStyle.NARROW, ResolverStyle.LENIENT, Locale.US, "05:59 in the morning", "06:00-12:00"}, 734 {TextStyle.NARROW, ResolverStyle.LENIENT, Locale.US, "11:59 n", "12:00"}, 735 {TextStyle.NARROW, ResolverStyle.LENIENT, Locale.US, "18:00 in the afternoon", "12:00-18:00"}, 736 {TextStyle.NARROW, ResolverStyle.LENIENT, Locale.US, "17:59 in the evening", "18:00-21:00"}, 737 738 {TextStyle.FULL, ResolverStyle.LENIENT, Locale.JAPAN, "00:01 \u771f\u591c\u4e2d", "00:00"}, 739 {TextStyle.FULL, ResolverStyle.LENIENT, Locale.JAPAN, "04:00 \u591c\u4e2d", "23:00-04:00"}, 740 {TextStyle.FULL, ResolverStyle.LENIENT, Locale.JAPAN, "03:59 \u671d", "04:00-12:00"}, 741 {TextStyle.FULL, ResolverStyle.LENIENT, Locale.JAPAN, "12:01 \u6b63\u5348", "12:00"}, 742 {TextStyle.FULL, ResolverStyle.LENIENT, Locale.JAPAN, "16:00 \u663c", "12:00-16:00"}, 743 {TextStyle.FULL, ResolverStyle.LENIENT, Locale.JAPAN, "19:01 \u5915\u65b9", "16:00-19:00"}, 744 {TextStyle.FULL, ResolverStyle.LENIENT, Locale.JAPAN, "23:00 \u591c", "19:00-23:00"}, 745 {TextStyle.NARROW, ResolverStyle.LENIENT, Locale.JAPAN, "00:01 \u771f\u591c\u4e2d", "00:00"}, 746 {TextStyle.NARROW, ResolverStyle.LENIENT, Locale.JAPAN, "04:00 \u591c\u4e2d", "23:00-04:00"}, 747 {TextStyle.NARROW, ResolverStyle.LENIENT, Locale.JAPAN, "03:59 \u671d", "04:00-12:00"}, 748 {TextStyle.NARROW, ResolverStyle.LENIENT, Locale.JAPAN, "12:01 \u6b63\u5348", "12:00"}, 749 {TextStyle.NARROW, ResolverStyle.LENIENT, Locale.JAPAN, "16:00 \u663c", "12:00-16:00"}, 750 {TextStyle.NARROW, ResolverStyle.LENIENT, Locale.JAPAN, "19:01 \u5915\u65b9", "16:00-19:00"}, 751 {TextStyle.NARROW, ResolverStyle.LENIENT, Locale.JAPAN, "23:00 \u591c", "19:00-23:00"}, 752 }; 753 } 754 @Test (dataProvider="dayPeriodParseInvalid") 755 public void test_dayPeriodParseInvalid(TextStyle ts, ResolverStyle rs, Locale l, String dayPeriod, String periodRange) throws Exception { 756 try { 757 builder.append(ISO_LOCAL_TIME).appendLiteral(' ').appendDayPeriodText(ts) 758 .toFormatter() 759 .withLocale(l) 760 .parse(dayPeriod); 761 if (rs != ResolverStyle.LENIENT) { 762 throw new RuntimeException("DateTimeParseException should be thrown"); 763 } 764 } catch (DateTimeParseException e) { 765 assertEquals(e.getCause().getMessage(), 766 "Conflict found: Resolved time " + dayPeriod.substring(0, 5) + " conflicts with " + 767 "DayPeriod(" + periodRange + ")"); 768 } 769 } 770 771 @DataProvider(name="dayPeriodParsePatternInvalid") 772 Object[][] data_dayPeriodParsePatternInvalid() { 773 return new Object[][] { 774 {"H B", ResolverStyle.SMART, "47 at night", 23, null}, 775 {"H B", ResolverStyle.SMART, "51 at night", 3, null}, 776 {"H B", ResolverStyle.SMART, "-2 at night", 22, null}, 777 {"K B", ResolverStyle.SMART, "59 at night", 23, null}, 778 {"K B", ResolverStyle.SMART, "51 at night", 3, null}, 779 {"K B", ResolverStyle.SMART, "59 in the morning", 11, null}, 780 {"K B", ResolverStyle.SMART, "-2 in the morning", 22, null}, 781 {"h B", ResolverStyle.SMART, "59 at night", 23, null}, 782 {"h B", ResolverStyle.SMART, "51 at night", 3, null}, 783 {"h B", ResolverStyle.SMART, "59 in the morning", 11, null}, 784 {"h B", ResolverStyle.SMART, "-2 in the morning", 22, null}, 785 786 {"H B", ResolverStyle.LENIENT, "47 at night", 23, Period.ofDays(1)}, 787 {"H B", ResolverStyle.LENIENT, "51 at night", 3, Period.ofDays(2)}, 788 {"H B", ResolverStyle.LENIENT, "-2 at night", 22, Period.ofDays(-1)}, 789 {"K B", ResolverStyle.LENIENT, "59 at night", 23, Period.ofDays(2)}, 790 {"K B", ResolverStyle.LENIENT, "51 at night", 3, Period.ofDays(2)}, 791 {"K B", ResolverStyle.LENIENT, "59 in the morning", 11, Period.ofDays(2)}, 792 {"K B", ResolverStyle.LENIENT, "-2 in the morning", 22, Period.ofDays(-1)}, 793 {"h B", ResolverStyle.LENIENT, "59 at night", 23, Period.ofDays(2)}, 794 {"h B", ResolverStyle.LENIENT, "51 at night", 3, Period.ofDays(2)}, 795 {"h B", ResolverStyle.LENIENT, "59 in the morning", 11, Period.ofDays(2)}, 796 {"h B", ResolverStyle.LENIENT, "-2 in the morning", 22, Period.ofDays(-1)}, 797 }; 798 } 799 800 @Test (dataProvider="dayPeriodParsePatternInvalid") 801 public void test_dayPeriodParsePatternInvalid(String pattern, ResolverStyle rs, String hourDayPeriod, long expected, Period expectedExcessDays) throws Exception { 802 try { 803 builder.appendPattern(pattern); 804 DateTimeFormatter f = builder.toFormatter().withLocale(Locale.US).withResolverStyle(rs); 805 var p = f.parse(hourDayPeriod); 806 if (rs != ResolverStyle.LENIENT) { 807 throw new RuntimeException("DateTimeParseException should be thrown"); 808 } 809 assertEquals(p.getLong(HOUR_OF_DAY), expected); 810 assertEquals(p.query(DateTimeFormatter.parsedExcessDays()), expectedExcessDays); 811 } catch (DateTimeParseException e) { 812 // exception successfully thrown 813 } 814 } 815 816 @Test (expectedExceptions = DateTimeParseException.class) 817 public void test_dayPeriodParseStrictNoTime() { 818 builder.appendPattern("B"); 819 DateTimeFormatter f = builder.toFormatter().withLocale(Locale.US).withResolverStyle(ResolverStyle.STRICT); 820 LocalTime.parse("at night", f); 821 } 822 823 @Test 824 public void test_dayPeriodUserFieldResolution() { 825 var dtf = builder 826 .appendValue(new TemporalField() { 827 @Override 828 public TemporalUnit getBaseUnit() { 829 return null; 830 } 831 832 @Override 833 public TemporalUnit getRangeUnit() { 834 return null; 835 } 836 837 @Override 838 public ValueRange range() { 839 return null; 840 } 841 842 @Override 843 public boolean isDateBased() { 844 return false; 845 } 846 847 @Override 848 public boolean isTimeBased() { 849 return false; 850 } 851 852 @Override 853 public boolean isSupportedBy(TemporalAccessor temporal) { 854 return false; 855 } 856 857 @Override 858 public ValueRange rangeRefinedBy(TemporalAccessor temporal) { 859 return null; 860 } 861 862 @Override 863 public long getFrom(TemporalAccessor temporal) { 864 return 0; 865 } 866 867 @Override 868 public <R extends Temporal> R adjustInto(R temporal, long newValue) { 869 return null; 870 } 871 872 @Override 873 public TemporalAccessor resolve( 874 Map<TemporalField, Long> fieldValues, 875 TemporalAccessor partialTemporal, 876 ResolverStyle resolverStyle) { 877 fieldValues.remove(this); 878 fieldValues.put(ChronoField.HOUR_OF_DAY, 6L); 879 return null; 880 } 881 }, 882 1) 883 .appendPattern(" B") 884 .toFormatter() 885 .withLocale(Locale.US); 886 assertEquals((long)dtf.parse("0 in the morning").getLong(ChronoField.HOUR_OF_DAY), 6L); 887 try { 888 dtf.parse("0 at night"); 889 fail("DateTimeParseException should be thrown"); 890 } catch (DateTimeParseException e) { 891 // success 892 } 893 } 894 */ 895 // END Android-removed: DateTimeFormatBuilder.appendDayPeriodText is temporarily unsupported. 896 //----------------------------------------------------------------------- 897 //----------------------------------------------------------------------- 898 //----------------------------------------------------------------------- 899 @Test test_padNext_1arg()900 public void test_padNext_1arg() { 901 builder.appendValue(MONTH_OF_YEAR).appendLiteral(':').padNext(2).appendValue(DAY_OF_MONTH); 902 assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2: 1"); 903 } 904 905 @Test(expectedExceptions=IllegalArgumentException.class) test_padNext_1arg_invalidWidth()906 public void test_padNext_1arg_invalidWidth() throws Exception { 907 builder.padNext(0); 908 } 909 910 //----------------------------------------------------------------------- 911 @Test test_padNext_2arg_dash()912 public void test_padNext_2arg_dash() throws Exception { 913 builder.appendValue(MONTH_OF_YEAR).appendLiteral(':').padNext(2, '-').appendValue(DAY_OF_MONTH); 914 assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2:-1"); 915 } 916 917 @Test(expectedExceptions=IllegalArgumentException.class) test_padNext_2arg_invalidWidth()918 public void test_padNext_2arg_invalidWidth() throws Exception { 919 builder.padNext(0, '-'); 920 } 921 922 //----------------------------------------------------------------------- 923 @Test test_padOptional()924 public void test_padOptional() throws Exception { 925 builder.appendValue(MONTH_OF_YEAR).appendLiteral(':') 926 .padNext(5).optionalStart().appendValue(DAY_OF_MONTH).optionalEnd() 927 .appendLiteral(':').appendValue(YEAR); 928 assertEquals(builder.toFormatter().format(LocalDate.of(2013, 2, 1)), "2: 1:2013"); 929 assertEquals(builder.toFormatter().format(YearMonth.of(2013, 2)), "2: :2013"); 930 } 931 932 //----------------------------------------------------------------------- 933 //----------------------------------------------------------------------- 934 //----------------------------------------------------------------------- 935 @Test test_optionalStart_noEnd()936 public void test_optionalStart_noEnd() throws Exception { 937 builder.appendValue(MONTH_OF_YEAR).optionalStart().appendValue(DAY_OF_MONTH).appendValue(DAY_OF_WEEK); 938 DateTimeFormatter f = builder.toFormatter(); 939 assertEquals(f.toString(), "Value(MonthOfYear)[Value(DayOfMonth)Value(DayOfWeek)]"); 940 } 941 942 @Test test_optionalStart2_noEnd()943 public void test_optionalStart2_noEnd() throws Exception { 944 builder.appendValue(MONTH_OF_YEAR).optionalStart().appendValue(DAY_OF_MONTH).optionalStart().appendValue(DAY_OF_WEEK); 945 DateTimeFormatter f = builder.toFormatter(); 946 assertEquals(f.toString(), "Value(MonthOfYear)[Value(DayOfMonth)[Value(DayOfWeek)]]"); 947 } 948 949 @Test test_optionalStart_doubleStart()950 public void test_optionalStart_doubleStart() throws Exception { 951 builder.appendValue(MONTH_OF_YEAR).optionalStart().optionalStart().appendValue(DAY_OF_MONTH); 952 DateTimeFormatter f = builder.toFormatter(); 953 assertEquals(f.toString(), "Value(MonthOfYear)[[Value(DayOfMonth)]]"); 954 } 955 956 //----------------------------------------------------------------------- 957 @Test test_optionalEnd()958 public void test_optionalEnd() throws Exception { 959 builder.appendValue(MONTH_OF_YEAR).optionalStart().appendValue(DAY_OF_MONTH).optionalEnd().appendValue(DAY_OF_WEEK); 960 DateTimeFormatter f = builder.toFormatter(); 961 assertEquals(f.toString(), "Value(MonthOfYear)[Value(DayOfMonth)]Value(DayOfWeek)"); 962 } 963 964 @Test test_optionalEnd2()965 public void test_optionalEnd2() throws Exception { 966 builder.appendValue(MONTH_OF_YEAR).optionalStart().appendValue(DAY_OF_MONTH) 967 .optionalStart().appendValue(DAY_OF_WEEK).optionalEnd().appendValue(DAY_OF_MONTH).optionalEnd(); 968 DateTimeFormatter f = builder.toFormatter(); 969 assertEquals(f.toString(), "Value(MonthOfYear)[Value(DayOfMonth)[Value(DayOfWeek)]Value(DayOfMonth)]"); 970 } 971 972 @Test test_optionalEnd_doubleStartSingleEnd()973 public void test_optionalEnd_doubleStartSingleEnd() throws Exception { 974 builder.appendValue(MONTH_OF_YEAR).optionalStart().optionalStart().appendValue(DAY_OF_MONTH).optionalEnd(); 975 DateTimeFormatter f = builder.toFormatter(); 976 assertEquals(f.toString(), "Value(MonthOfYear)[[Value(DayOfMonth)]]"); 977 } 978 979 @Test test_optionalEnd_doubleStartDoubleEnd()980 public void test_optionalEnd_doubleStartDoubleEnd() throws Exception { 981 builder.appendValue(MONTH_OF_YEAR).optionalStart().optionalStart().appendValue(DAY_OF_MONTH).optionalEnd().optionalEnd(); 982 DateTimeFormatter f = builder.toFormatter(); 983 assertEquals(f.toString(), "Value(MonthOfYear)[[Value(DayOfMonth)]]"); 984 } 985 986 @Test test_optionalStartEnd_immediateStartEnd()987 public void test_optionalStartEnd_immediateStartEnd() throws Exception { 988 builder.appendValue(MONTH_OF_YEAR).optionalStart().optionalEnd().appendValue(DAY_OF_MONTH); 989 DateTimeFormatter f = builder.toFormatter(); 990 assertEquals(f.toString(), "Value(MonthOfYear)Value(DayOfMonth)"); 991 } 992 993 @Test(expectedExceptions=IllegalStateException.class) test_optionalEnd_noStart()994 public void test_optionalEnd_noStart() throws Exception { 995 builder.optionalEnd(); 996 } 997 998 //----------------------------------------------------------------------- 999 //----------------------------------------------------------------------- 1000 //----------------------------------------------------------------------- 1001 @DataProvider(name="validPatterns") dataValid()1002 Object[][] dataValid() { 1003 return new Object[][] { 1004 {"'a'", "'a'"}, 1005 {"''", "''"}, 1006 {"'!'", "'!'"}, 1007 {"!", "'!'"}, 1008 1009 {"'hello_people,][)('", "'hello_people,][)('"}, 1010 {"'hi'", "'hi'"}, 1011 {"'yyyy'", "'yyyy'"}, 1012 {"''''", "''"}, 1013 {"'o''clock'", "'o''clock'"}, 1014 1015 {"G", "Text(Era,SHORT)"}, 1016 {"GG", "Text(Era,SHORT)"}, 1017 {"GGG", "Text(Era,SHORT)"}, 1018 {"GGGG", "Text(Era)"}, 1019 {"GGGGG", "Text(Era,NARROW)"}, 1020 1021 {"u", "Value(Year)"}, 1022 {"uu", "ReducedValue(Year,2,2,2000-01-01)"}, 1023 {"uuu", "Value(Year,3,19,NORMAL)"}, 1024 {"uuuu", "Value(Year,4,19,EXCEEDS_PAD)"}, 1025 {"uuuuu", "Value(Year,5,19,EXCEEDS_PAD)"}, 1026 1027 {"y", "Value(YearOfEra)"}, 1028 {"yy", "ReducedValue(YearOfEra,2,2,2000-01-01)"}, 1029 {"yyy", "Value(YearOfEra,3,19,NORMAL)"}, 1030 {"yyyy", "Value(YearOfEra,4,19,EXCEEDS_PAD)"}, 1031 {"yyyyy", "Value(YearOfEra,5,19,EXCEEDS_PAD)"}, 1032 1033 {"Y", "Localized(WeekBasedYear)"}, 1034 {"YY", "Localized(ReducedValue(WeekBasedYear,2,2,2000-01-01))"}, 1035 {"YYY", "Localized(WeekBasedYear,3,19,NORMAL)"}, 1036 {"YYYY", "Localized(WeekBasedYear,4,19,EXCEEDS_PAD)"}, 1037 {"YYYYY", "Localized(WeekBasedYear,5,19,EXCEEDS_PAD)"}, 1038 1039 {"M", "Value(MonthOfYear)"}, 1040 {"MM", "Value(MonthOfYear,2)"}, 1041 {"MMM", "Text(MonthOfYear,SHORT)"}, 1042 {"MMMM", "Text(MonthOfYear)"}, 1043 {"MMMMM", "Text(MonthOfYear,NARROW)"}, 1044 1045 {"L", "Value(MonthOfYear)"}, 1046 {"LL", "Value(MonthOfYear,2)"}, 1047 {"LLL", "Text(MonthOfYear,SHORT_STANDALONE)"}, 1048 {"LLLL", "Text(MonthOfYear,FULL_STANDALONE)"}, 1049 {"LLLLL", "Text(MonthOfYear,NARROW_STANDALONE)"}, 1050 1051 {"D", "Value(DayOfYear)"}, 1052 {"DD", "Value(DayOfYear,2,3,NOT_NEGATIVE)"}, 1053 {"DDD", "Value(DayOfYear,3)"}, 1054 1055 {"d", "Value(DayOfMonth)"}, 1056 {"dd", "Value(DayOfMonth,2)"}, 1057 1058 {"F", "Value(AlignedDayOfWeekInMonth)"}, 1059 1060 {"Q", "Value(QuarterOfYear)"}, 1061 {"QQ", "Value(QuarterOfYear,2)"}, 1062 {"QQQ", "Text(QuarterOfYear,SHORT)"}, 1063 {"QQQQ", "Text(QuarterOfYear)"}, 1064 {"QQQQQ", "Text(QuarterOfYear,NARROW)"}, 1065 1066 {"q", "Value(QuarterOfYear)"}, 1067 {"qq", "Value(QuarterOfYear,2)"}, 1068 {"qqq", "Text(QuarterOfYear,SHORT_STANDALONE)"}, 1069 {"qqqq", "Text(QuarterOfYear,FULL_STANDALONE)"}, 1070 {"qqqqq", "Text(QuarterOfYear,NARROW_STANDALONE)"}, 1071 1072 {"E", "Text(DayOfWeek,SHORT)"}, 1073 {"EE", "Text(DayOfWeek,SHORT)"}, 1074 {"EEE", "Text(DayOfWeek,SHORT)"}, 1075 {"EEEE", "Text(DayOfWeek)"}, 1076 {"EEEEE", "Text(DayOfWeek,NARROW)"}, 1077 1078 {"e", "Localized(DayOfWeek,1)"}, 1079 {"ee", "Localized(DayOfWeek,2)"}, 1080 {"eee", "Text(DayOfWeek,SHORT)"}, 1081 {"eeee", "Text(DayOfWeek)"}, 1082 {"eeeee", "Text(DayOfWeek,NARROW)"}, 1083 1084 {"c", "Localized(DayOfWeek,1)"}, 1085 {"ccc", "Text(DayOfWeek,SHORT_STANDALONE)"}, 1086 {"cccc", "Text(DayOfWeek,FULL_STANDALONE)"}, 1087 {"ccccc", "Text(DayOfWeek,NARROW_STANDALONE)"}, 1088 1089 {"a", "Text(AmPmOfDay,SHORT)"}, 1090 1091 {"H", "Value(HourOfDay)"}, 1092 {"HH", "Value(HourOfDay,2)"}, 1093 1094 {"K", "Value(HourOfAmPm)"}, 1095 {"KK", "Value(HourOfAmPm,2)"}, 1096 1097 {"k", "Value(ClockHourOfDay)"}, 1098 {"kk", "Value(ClockHourOfDay,2)"}, 1099 1100 {"h", "Value(ClockHourOfAmPm)"}, 1101 {"hh", "Value(ClockHourOfAmPm,2)"}, 1102 1103 {"m", "Value(MinuteOfHour)"}, 1104 {"mm", "Value(MinuteOfHour,2)"}, 1105 1106 {"s", "Value(SecondOfMinute)"}, 1107 {"ss", "Value(SecondOfMinute,2)"}, 1108 1109 {"S", "Fraction(NanoOfSecond,1,1)"}, 1110 {"SS", "Fraction(NanoOfSecond,2,2)"}, 1111 {"SSS", "Fraction(NanoOfSecond,3,3)"}, 1112 {"SSSSSSSSS", "Fraction(NanoOfSecond,9,9)"}, 1113 1114 {"A", "Value(MilliOfDay,1,19,NOT_NEGATIVE)"}, 1115 {"AA", "Value(MilliOfDay,2,19,NOT_NEGATIVE)"}, 1116 {"AAA", "Value(MilliOfDay,3,19,NOT_NEGATIVE)"}, 1117 1118 {"n", "Value(NanoOfSecond,1,19,NOT_NEGATIVE)"}, 1119 {"nn", "Value(NanoOfSecond,2,19,NOT_NEGATIVE)"}, 1120 {"nnn", "Value(NanoOfSecond,3,19,NOT_NEGATIVE)"}, 1121 1122 {"N", "Value(NanoOfDay,1,19,NOT_NEGATIVE)"}, 1123 {"NN", "Value(NanoOfDay,2,19,NOT_NEGATIVE)"}, 1124 {"NNN", "Value(NanoOfDay,3,19,NOT_NEGATIVE)"}, 1125 1126 {"z", "ZoneText(SHORT)"}, 1127 {"zz", "ZoneText(SHORT)"}, 1128 {"zzz", "ZoneText(SHORT)"}, 1129 {"zzzz", "ZoneText(FULL)"}, 1130 1131 {"VV", "ZoneId()"}, 1132 1133 {"Z", "Offset(+HHMM,'+0000')"}, // SimpleDateFormat 1134 {"ZZ", "Offset(+HHMM,'+0000')"}, // SimpleDateFormat 1135 {"ZZZ", "Offset(+HHMM,'+0000')"}, // SimpleDateFormat 1136 1137 {"X", "Offset(+HHmm,'Z')"}, // LDML/almost SimpleDateFormat 1138 {"XX", "Offset(+HHMM,'Z')"}, // LDML/SimpleDateFormat 1139 {"XXX", "Offset(+HH:MM,'Z')"}, // LDML/SimpleDateFormat 1140 {"XXXX", "Offset(+HHMMss,'Z')"}, // LDML 1141 {"XXXXX", "Offset(+HH:MM:ss,'Z')"}, // LDML 1142 1143 {"x", "Offset(+HHmm,'+00')"}, // LDML 1144 {"xx", "Offset(+HHMM,'+0000')"}, // LDML 1145 {"xxx", "Offset(+HH:MM,'+00:00')"}, // LDML 1146 {"xxxx", "Offset(+HHMMss,'+0000')"}, // LDML 1147 {"xxxxx", "Offset(+HH:MM:ss,'+00:00')"}, // LDML 1148 1149 {"ppH", "Pad(Value(HourOfDay),2)"}, 1150 {"pppDD", "Pad(Value(DayOfYear,2,3,NOT_NEGATIVE),3)"}, 1151 1152 {"yyyy[-MM[-dd", "Value(YearOfEra,4,19,EXCEEDS_PAD)['-'Value(MonthOfYear,2)['-'Value(DayOfMonth,2)]]"}, 1153 {"yyyy[-MM[-dd]]", "Value(YearOfEra,4,19,EXCEEDS_PAD)['-'Value(MonthOfYear,2)['-'Value(DayOfMonth,2)]]"}, 1154 {"yyyy[-MM[]-dd]", "Value(YearOfEra,4,19,EXCEEDS_PAD)['-'Value(MonthOfYear,2)'-'Value(DayOfMonth,2)]"}, 1155 1156 {"yyyy-MM-dd'T'HH:mm:ss.SSS", "Value(YearOfEra,4,19,EXCEEDS_PAD)'-'Value(MonthOfYear,2)'-'Value(DayOfMonth,2)" + 1157 "'T'Value(HourOfDay,2)':'Value(MinuteOfHour,2)':'Value(SecondOfMinute,2)'.'Fraction(NanoOfSecond,3,3)"}, 1158 1159 {"w", "Localized(WeekOfWeekBasedYear,1)"}, 1160 {"ww", "Localized(WeekOfWeekBasedYear,2)"}, 1161 {"W", "Localized(WeekOfMonth,1)"}, 1162 1163 // Android-removed: The symbol 'B' is temporarily unsupported. 1164 // {"B", "DayPeriod(SHORT)"}, 1165 // {"BBBB", "DayPeriod(FULL)"}, 1166 // {"BBBBB", "DayPeriod(NARROW)"}, 1167 }; 1168 } 1169 1170 @Test(dataProvider="validPatterns") test_appendPattern_valid(String input, String expected)1171 public void test_appendPattern_valid(String input, String expected) throws Exception { 1172 builder.appendPattern(input); 1173 DateTimeFormatter f = builder.toFormatter(); 1174 assertEquals(f.toString(), expected); 1175 } 1176 1177 //----------------------------------------------------------------------- 1178 @DataProvider(name="invalidPatterns") dataInvalid()1179 Object[][] dataInvalid() { 1180 return new Object[][] { 1181 {"'"}, 1182 {"'hello"}, 1183 {"'hel''lo"}, 1184 {"'hello''"}, 1185 {"{"}, 1186 {"}"}, 1187 {"{}"}, 1188 {"]"}, 1189 {"yyyy]"}, 1190 {"yyyy]MM"}, 1191 {"yyyy[MM]]"}, 1192 1193 {"aa"}, 1194 {"aaa"}, 1195 {"aaaa"}, 1196 {"aaaaa"}, 1197 {"aaaaaa"}, 1198 {"MMMMMM"}, 1199 {"LLLLLL"}, 1200 {"QQQQQQ"}, 1201 {"qqqqqq"}, 1202 {"EEEEEE"}, 1203 {"eeeeee"}, 1204 {"cc"}, 1205 {"cccccc"}, 1206 {"ddd"}, 1207 {"DDDD"}, 1208 {"FF"}, 1209 {"FFF"}, 1210 {"hhh"}, 1211 {"HHH"}, 1212 {"kkk"}, 1213 {"KKK"}, 1214 {"mmm"}, 1215 {"sss"}, 1216 {"OO"}, 1217 {"OOO"}, 1218 {"OOOOO"}, 1219 {"XXXXXX"}, 1220 {"ZZZZZZ"}, 1221 {"zzzzz"}, 1222 {"V"}, 1223 {"VVV"}, 1224 {"VVVV"}, 1225 {"VVVVV"}, 1226 1227 {"RO"}, 1228 1229 {"p"}, 1230 {"pp"}, 1231 {"p:"}, 1232 1233 {"f"}, 1234 {"ff"}, 1235 {"f:"}, 1236 {"fy"}, 1237 {"fa"}, 1238 {"fM"}, 1239 1240 {"www"}, 1241 {"WW"}, 1242 1243 {"BB"}, 1244 {"BBB"}, 1245 {"BBBBBB"}, 1246 }; 1247 } 1248 1249 @Test(dataProvider="invalidPatterns", expectedExceptions=IllegalArgumentException.class) test_appendPattern_invalid(String input)1250 public void test_appendPattern_invalid(String input) throws Exception { 1251 try { 1252 builder.appendPattern(input); 1253 } catch (IllegalArgumentException ex) { 1254 throw ex; 1255 } 1256 } 1257 1258 //----------------------------------------------------------------------- 1259 @DataProvider(name="localePatterns") localizedDateTimePatterns()1260 Object[][] localizedDateTimePatterns() { 1261 // Android-changed: Adapt for changes since old CLDR version this tests were written for. 1262 return new Object[][] { 1263 {FormatStyle.FULL, FormatStyle.FULL, IsoChronology.INSTANCE, Locale.US, "EEEE, MMMM d, y 'at' h:mm:ss\u202fa zzzz"}, 1264 {FormatStyle.LONG, FormatStyle.LONG, IsoChronology.INSTANCE, Locale.US, "MMMM d, y 'at' h:mm:ss\u202fa z"}, 1265 {FormatStyle.MEDIUM, FormatStyle.MEDIUM, IsoChronology.INSTANCE, Locale.US, "MMM d, y, h:mm:ss\u202fa"}, 1266 {FormatStyle.SHORT, FormatStyle.SHORT, IsoChronology.INSTANCE, Locale.US, "M/d/yy, h:mm\u202fa"}, 1267 {FormatStyle.FULL, null, IsoChronology.INSTANCE, Locale.US, "EEEE, MMMM d, y"}, 1268 {FormatStyle.LONG, null, IsoChronology.INSTANCE, Locale.US, "MMMM d, y"}, 1269 {FormatStyle.MEDIUM, null, IsoChronology.INSTANCE, Locale.US, "MMM d, y"}, 1270 {FormatStyle.SHORT, null, IsoChronology.INSTANCE, Locale.US, "M/d/yy"}, 1271 {null, FormatStyle.FULL, IsoChronology.INSTANCE, Locale.US, "h:mm:ss\u202fa zzzz"}, 1272 {null, FormatStyle.LONG, IsoChronology.INSTANCE, Locale.US, "h:mm:ss\u202fa z"}, 1273 {null, FormatStyle.MEDIUM, IsoChronology.INSTANCE, Locale.US, "h:mm:ss\u202fa"}, 1274 {null, FormatStyle.SHORT, IsoChronology.INSTANCE, Locale.US, "h:mm\u202fa"}, 1275 }; 1276 } 1277 1278 @Test(dataProvider="localePatterns") test_getLocalizedDateTimePattern(FormatStyle dateStyle, FormatStyle timeStyle, Chronology chrono, Locale locale, String expected)1279 public void test_getLocalizedDateTimePattern(FormatStyle dateStyle, FormatStyle timeStyle, 1280 Chronology chrono, Locale locale, String expected) { 1281 // Android-changed: Require ICU 72 to pass the test due to CLDR data change 1282 if (VersionInfo.ICU_VERSION.getMajor() < 72) { 1283 return; 1284 } 1285 String actual = DateTimeFormatterBuilder.getLocalizedDateTimePattern(dateStyle, timeStyle, chrono, locale); 1286 assertEquals(actual, expected, "Pattern " + convertNonAscii(actual)); 1287 } 1288 1289 @Test(expectedExceptions=java.lang.IllegalArgumentException.class) test_getLocalizedDateTimePatternIAE()1290 public void test_getLocalizedDateTimePatternIAE() { 1291 DateTimeFormatterBuilder.getLocalizedDateTimePattern(null, null, IsoChronology.INSTANCE, Locale.US); 1292 } 1293 1294 @Test(expectedExceptions=java.lang.NullPointerException.class) test_getLocalizedChronoNPE()1295 public void test_getLocalizedChronoNPE() { 1296 DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.SHORT, FormatStyle.SHORT, null, Locale.US); 1297 } 1298 1299 @Test(expectedExceptions=java.lang.NullPointerException.class) test_getLocalizedLocaleNPE()1300 public void test_getLocalizedLocaleNPE() { 1301 DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.SHORT, FormatStyle.SHORT, IsoChronology.INSTANCE, null); 1302 } 1303 1304 /** 1305 * Returns a string that includes non-ascii characters after expanding 1306 * the non-ascii characters to their Java language \\uxxxx form. 1307 * @param input an input string 1308 * @return the encoded string. 1309 */ convertNonAscii(String input)1310 private String convertNonAscii(String input) { 1311 StringBuilder sb = new StringBuilder(input.length() * 6); 1312 for (int i = 0; i < input.length(); i++) { 1313 char ch = input.charAt(i); 1314 if (ch < 255) { 1315 sb.append(ch); 1316 } else { 1317 sb.append("\\u"); 1318 sb.append(Integer.toHexString(ch)); 1319 } 1320 } 1321 return sb.toString(); 1322 } 1323 date(int y, int m, int d)1324 private static Temporal date(int y, int m, int d) { 1325 return LocalDate.of(y, m, d); 1326 } 1327 } 1328