1 /* 2 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* 27 * This file is available under and governed by the GNU General Public 28 * License version 2 only, as published by the Free Software Foundation. 29 * However, the following notice accompanied the original version of this 30 * file: 31 * 32 * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos 33 * 34 * All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions are met: 38 * 39 * * Redistributions of source code must retain the above copyright notice, 40 * this list of conditions and the following disclaimer. 41 * 42 * * Redistributions in binary form must reproduce the above copyright notice, 43 * this list of conditions and the following disclaimer in the documentation 44 * and/or other materials provided with the distribution. 45 * 46 * * Neither the name of JSR-310 nor the names of its contributors 47 * may be used to endorse or promote products derived from this software 48 * without specific prior written permission. 49 * 50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 54 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 55 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 56 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 57 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 58 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 59 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 60 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 61 */ 62 package java.time.format; 63 64 import java.io.IOException; 65 import java.text.FieldPosition; 66 import java.text.Format; 67 import java.text.ParseException; 68 import java.text.ParsePosition; 69 import java.time.DateTimeException; 70 import java.time.ZoneId; 71 import java.time.format.DateTimeFormatterBuilder.CompositePrinterParser; 72 import java.time.temporal.Chrono; 73 import java.time.temporal.TemporalAccessor; 74 import java.time.temporal.TemporalQuery; 75 import java.util.Locale; 76 import java.util.Objects; 77 78 /** 79 * Formatter for printing and parsing date-time objects. 80 * <p> 81 * This class provides the main application entry point for printing and parsing. 82 * Common instances of {@code DateTimeFormatter} are provided by {@link DateTimeFormatters}. 83 * For more complex formatters, a {@link DateTimeFormatterBuilder builder} is provided. 84 * <p> 85 * In most cases, it is not necessary to use this class directly when formatting. 86 * The main date-time classes provide two methods - one for printing, 87 * {@code toString(DateTimeFormatter formatter)}, and one for parsing, 88 * {@code parse(CharSequence text, DateTimeFormatter formatter)}. 89 * For example: 90 * <pre> 91 * String text = date.toString(formatter); 92 * LocalDate date = LocalDate.parse(text, formatter); 93 * </pre> 94 * Some aspects of printing and parsing are dependent on the locale. 95 * The locale can be changed using the {@link #withLocale(Locale)} method 96 * which returns a new formatter in the requested locale. 97 * <p> 98 * Some applications may need to use the older {@link Format} class for formatting. 99 * The {@link #toFormat()} method returns an implementation of the old API. 100 * 101 * <h3>Specification for implementors</h3> 102 * This class is immutable and thread-safe. 103 * 104 * @since 1.8 105 */ 106 public final class DateTimeFormatter { 107 108 /** 109 * The printer and/or parser to use, not null. 110 */ 111 private final CompositePrinterParser printerParser; 112 /** 113 * The locale to use for formatting, not null. 114 */ 115 private final Locale locale; 116 /** 117 * The symbols to use for formatting, not null. 118 */ 119 private final DateTimeFormatSymbols symbols; 120 /** 121 * The chronology to use for formatting, null for no override. 122 */ 123 private final Chrono<?> chrono; 124 /** 125 * The zone to use for formatting, null for no override. 126 */ 127 private final ZoneId zone; 128 129 /** 130 * Constructor. 131 * 132 * @param printerParser the printer/parser to use, not null 133 * @param locale the locale to use, not null 134 * @param symbols the symbols to use, not null 135 * @param chrono the chronology to use, null for no override 136 * @param zone the zone to use, null for no override 137 */ 138 DateTimeFormatter(CompositePrinterParser printerParser, Locale locale, 139 DateTimeFormatSymbols symbols, Chrono chrono, ZoneId zone) { 140 this.printerParser = Objects.requireNonNull(printerParser, "printerParser"); 141 this.locale = Objects.requireNonNull(locale, "locale"); 142 this.symbols = Objects.requireNonNull(symbols, "symbols"); 143 this.chrono = chrono; 144 this.zone = zone; 145 } 146 147 //----------------------------------------------------------------------- 148 /** 149 * Gets the locale to be used during formatting. 150 * <p> 151 * This is used to lookup any part of the formatter needing specific 152 * localization, such as the text or localized pattern. 153 * 154 * @return the locale of this formatter, not null 155 */ 156 public Locale getLocale() { 157 return locale; 158 } 159 160 /** 161 * Returns a copy of this formatter with a new locale. 162 * <p> 163 * This is used to lookup any part of the formatter needing specific 164 * localization, such as the text or localized pattern. 165 * <p> 166 * This instance is immutable and unaffected by this method call. 167 * 168 * @param locale the new locale, not null 169 * @return a formatter based on this formatter with the requested locale, not null 170 */ 171 public DateTimeFormatter withLocale(Locale locale) { 172 if (this.locale.equals(locale)) { 173 return this; 174 } 175 return new DateTimeFormatter(printerParser, locale, symbols, chrono, zone); 176 } 177 178 //----------------------------------------------------------------------- 179 /** 180 * Gets the set of symbols to be used during formatting. 181 * 182 * @return the locale of this formatter, not null 183 */ 184 public DateTimeFormatSymbols getSymbols() { 185 return symbols; 186 } 187 188 /** 189 * Returns a copy of this formatter with a new set of symbols. 190 * <p> 191 * This instance is immutable and unaffected by this method call. 192 * 193 * @param symbols the new symbols, not null 194 * @return a formatter based on this formatter with the requested symbols, not null 195 */ 196 public DateTimeFormatter withSymbols(DateTimeFormatSymbols symbols) { 197 if (this.symbols.equals(symbols)) { 198 return this; 199 } 200 return new DateTimeFormatter(printerParser, locale, symbols, chrono, zone); 201 } 202 203 //----------------------------------------------------------------------- 204 /** 205 * Gets the overriding chronology to be used during formatting. 206 * <p> 207 * This returns the override chronology, used to convert dates. 208 * By default, a formatter has no override chronology, returning null. 209 * See {@link #withChrono(Chrono)} for more details on overriding. 210 * 211 * @return the chronology of this formatter, null if no override 212 */ 213 public Chrono<?> getChrono() { 214 return chrono; 215 } 216 217 /** 218 * Returns a copy of this formatter with a new override chronology. 219 * <p> 220 * This returns a formatter with similar state to this formatter but 221 * with the override chronology set. 222 * By default, a formatter has no override chronology, returning null. 223 * <p> 224 * If an override is added, then any date that is printed or parsed will be affected. 225 * <p> 226 * When printing, if the {@code Temporal} object contains a date then it will 227 * be converted to a date in the override chronology. 228 * Any time or zone will be retained unless overridden. 229 * The converted result will behave in a manner equivalent to an implementation 230 * of {@code ChronoLocalDate},{@code ChronoLocalDateTime} or {@code ChronoZonedDateTime}. 231 * <p> 232 * When parsing, the override chronology will be used to interpret the 233 * {@link java.time.temporal.ChronoField fields} into a date unless the 234 * formatter directly parses a valid chronology. 235 * <p> 236 * This instance is immutable and unaffected by this method call. 237 * 238 * @param chrono the new chronology, not null 239 * @return a formatter based on this formatter with the requested override chronology, not null 240 */ 241 public DateTimeFormatter withChrono(Chrono chrono) { 242 if (Objects.equals(this.chrono, chrono)) { 243 return this; 244 } 245 return new DateTimeFormatter(printerParser, locale, symbols, chrono, zone); 246 } 247 248 //----------------------------------------------------------------------- 249 /** 250 * Gets the overriding zone to be used during formatting. 251 * <p> 252 * This returns the override zone, used to convert instants. 253 * By default, a formatter has no override zone, returning null. 254 * See {@link #withZone(ZoneId)} for more details on overriding. 255 * 256 * @return the chronology of this formatter, null if no override 257 */ 258 public ZoneId getZone() { 259 return zone; 260 } 261 262 /** 263 * Returns a copy of this formatter with a new override zone. 264 * <p> 265 * This returns a formatter with similar state to this formatter but 266 * with the override zone set. 267 * By default, a formatter has no override zone, returning null. 268 * <p> 269 * If an override is added, then any instant that is printed or parsed will be affected. 270 * <p> 271 * When printing, if the {@code Temporal} object contains an instant then it will 272 * be converted to a zoned date-time using the override zone. 273 * If the input has a chronology then it will be retained unless overridden. 274 * If the input does not have a chronology, such as {@code Instant}, then 275 * the ISO chronology will be used. 276 * The converted result will behave in a manner equivalent to an implementation 277 * of {@code ChronoZonedDateTime}. 278 * <p> 279 * When parsing, the override zone will be used to interpret the 280 * {@link java.time.temporal.ChronoField fields} into an instant unless the 281 * formatter directly parses a valid zone. 282 * <p> 283 * This instance is immutable and unaffected by this method call. 284 * 285 * @param zone the new override zone, not null 286 * @return a formatter based on this formatter with the requested override zone, not null 287 */ 288 public DateTimeFormatter withZone(ZoneId zone) { 289 if (Objects.equals(this.zone, zone)) { 290 return this; 291 } 292 return new DateTimeFormatter(printerParser, locale, symbols, chrono, zone); 293 } 294 295 //----------------------------------------------------------------------- 296 /** 297 * Prints a date-time object using this formatter. 298 * <p> 299 * This prints the date-time to a String using the rules of the formatter. 300 * 301 * @param temporal the temporal object to print, not null 302 * @return the printed string, not null 303 * @throws DateTimeException if an error occurs during printing 304 */ 305 public String print(TemporalAccessor temporal) { 306 StringBuilder buf = new StringBuilder(32); 307 printTo(temporal, buf); 308 return buf.toString(); 309 } 310 311 //----------------------------------------------------------------------- 312 /** 313 * Prints a date-time object to an {@code Appendable} using this formatter. 314 * <p> 315 * This prints the date-time to the specified destination. 316 * {@link Appendable} is a general purpose interface that is implemented by all 317 * key character output classes including {@code StringBuffer}, {@code StringBuilder}, 318 * {@code PrintStream} and {@code Writer}. 319 * <p> 320 * Although {@code Appendable} methods throw an {@code IOException}, this method does not. 321 * Instead, any {@code IOException} is wrapped in a runtime exception. 322 * See {@link DateTimePrintException#rethrowIOException()} for a means 323 * to extract the {@code IOException}. 324 * 325 * @param temporal the temporal object to print, not null 326 * @param appendable the appendable to print to, not null 327 * @throws DateTimeException if an error occurs during printing 328 */ 329 public void printTo(TemporalAccessor temporal, Appendable appendable) { 330 Objects.requireNonNull(temporal, "temporal"); 331 Objects.requireNonNull(appendable, "appendable"); 332 try { 333 DateTimePrintContext context = new DateTimePrintContext(temporal, this); 334 if (appendable instanceof StringBuilder) { 335 printerParser.print(context, (StringBuilder) appendable); 336 } else { 337 // buffer output to avoid writing to appendable in case of error 338 StringBuilder buf = new StringBuilder(32); 339 printerParser.print(context, buf); 340 appendable.append(buf); 341 } 342 } catch (IOException ex) { 343 throw new DateTimePrintException(ex.getMessage(), ex); 344 } 345 } 346 347 //----------------------------------------------------------------------- 348 /** 349 * Fully parses the text producing an object of the specified type. 350 * <p> 351 * Most applications should use this method for parsing. 352 * It parses the entire text to produce the required date-time. 353 * The query is typically a method reference to a {@code from(TemporalAccessor)} method. 354 * For example: 355 * <pre> 356 * LocalDateTime dt = parser.parse(str, LocalDateTime::from); 357 * </pre> 358 * If the parse completes without reading the entire length of the text, 359 * or a problem occurs during parsing or merging, then an exception is thrown. 360 * 361 * @param <T> the type of the parsed date-time 362 * @param text the text to parse, not null 363 * @param query the query defining the type to parse to, not null 364 * @return the parsed date-time, not null 365 * @throws DateTimeParseException if the parse fails 366 */ 367 public <T> T parse(CharSequence text, TemporalQuery<T> query) { 368 Objects.requireNonNull(text, "text"); 369 Objects.requireNonNull(query, "query"); 370 String str = text.toString(); // parsing whole String, so this makes sense 371 try { 372 DateTimeBuilder builder = parseToBuilder(str).resolve(); 373 return builder.query(query); 374 } catch (DateTimeParseException ex) { 375 throw ex; 376 } catch (RuntimeException ex) { 377 throw createError(str, ex); 378 } 379 } 380 381 /** 382 * Fully parses the text producing an object of one of the specified types. 383 * <p> 384 * This parse method is convenient for use when the parser can handle optional elements. 385 * For example, a pattern of 'yyyy-MM[-dd[Z]]' can be fully parsed to an {@code OffsetDate}, 386 * or partially parsed to a {@code LocalDate} or a {@code YearMonth}. 387 * The queries must be specified in order, starting from the best matching full-parse option 388 * and ending with the worst matching minimal parse option. 389 * The query is typically a method reference to a {@code from(TemporalAccessor)} method. 390 * <p> 391 * The result is associated with the first type that successfully parses. 392 * Normally, applications will use {@code instanceof} to check the result. 393 * For example: 394 * <pre> 395 * TemporalAccessor dt = parser.parseBest(str, OffsetDate::from, LocalDate::from); 396 * if (dt instanceof OffsetDate) { 397 * ... 398 * } else { 399 * ... 400 * } 401 * </pre> 402 * If the parse completes without reading the entire length of the text, 403 * or a problem occurs during parsing or merging, then an exception is thrown. 404 * 405 * @param text the text to parse, not null 406 * @param queries the queries defining the types to attempt to parse to, 407 * must implement {@code TemporalAccessor}, not null 408 * @return the parsed date-time, not null 409 * @throws IllegalArgumentException if less than 2 types are specified 410 * @throws DateTimeException if none of the queries can be parsed from the input 411 * @throws DateTimeParseException if the parse fails 412 */ 413 public TemporalAccessor parseBest(CharSequence text, TemporalQuery<?>... queries) { 414 Objects.requireNonNull(text, "text"); 415 Objects.requireNonNull(queries, "queries"); 416 if (queries.length < 2) { 417 throw new IllegalArgumentException("At least two queries must be specified"); 418 } 419 String str = text.toString(); // parsing whole String, so this makes sense 420 try { 421 DateTimeBuilder builder = parseToBuilder(str).resolve(); 422 for (TemporalQuery<?> query : queries) { 423 try { 424 return (TemporalAccessor) builder.query(query); 425 } catch (RuntimeException ex) { 426 // continue 427 } 428 } 429 throw new DateTimeException("Unable to convert parsed text using any of the specified queries"); 430 } catch (DateTimeParseException ex) { 431 throw ex; 432 } catch (RuntimeException ex) { 433 throw createError(str, ex); 434 } 435 } 436 437 private DateTimeParseException createError(String str, RuntimeException ex) { 438 String abbr = str; 439 if (abbr.length() > 64) { 440 abbr = abbr.substring(0, 64) + "..."; 441 } 442 return new DateTimeParseException("Text '" + abbr + "' could not be parsed: " + ex.getMessage(), str, 0, ex); 443 } 444 445 //----------------------------------------------------------------------- 446 /** 447 * Parses the text to a builder. 448 * <p> 449 * This parses to a {@code DateTimeBuilder} ensuring that the text is fully parsed. 450 * This method throws {@link DateTimeParseException} if unable to parse, or 451 * some other {@code DateTimeException} if another date/time problem occurs. 452 * 453 * @param text the text to parse, not null 454 * @return the engine representing the result of the parse, not null 455 * @throws DateTimeParseException if the parse fails 456 */ 457 public DateTimeBuilder parseToBuilder(CharSequence text) { 458 Objects.requireNonNull(text, "text"); 459 String str = text.toString(); // parsing whole String, so this makes sense 460 ParsePosition pos = new ParsePosition(0); 461 DateTimeBuilder result = parseToBuilder(str, pos); 462 if (result == null || pos.getErrorIndex() >= 0 || pos.getIndex() < str.length()) { 463 String abbr = str; 464 if (abbr.length() > 64) { 465 abbr = abbr.substring(0, 64) + "..."; 466 } 467 if (pos.getErrorIndex() >= 0) { 468 throw new DateTimeParseException("Text '" + abbr + "' could not be parsed at index " + 469 pos.getErrorIndex(), str, pos.getErrorIndex()); 470 } else { 471 throw new DateTimeParseException("Text '" + abbr + "' could not be parsed, unparsed text found at index " + 472 pos.getIndex(), str, pos.getIndex()); 473 } 474 } 475 return result; 476 } 477 478 /** 479 * Parses the text to a builder. 480 * <p> 481 * This parses to a {@code DateTimeBuilder} but does not require the input to be fully parsed. 482 * <p> 483 * This method does not throw {@link DateTimeParseException}. 484 * Instead, errors are returned within the state of the specified parse position. 485 * Callers must check for errors before using the context. 486 * <p> 487 * This method may throw some other {@code DateTimeException} if a date/time problem occurs. 488 * 489 * @param text the text to parse, not null 490 * @param position the position to parse from, updated with length parsed 491 * and the index of any error, not null 492 * @return the parsed text, null only if the parse results in an error 493 * @throws DateTimeException if some problem occurs during parsing 494 * @throws IndexOutOfBoundsException if the position is invalid 495 */ 496 public DateTimeBuilder parseToBuilder(CharSequence text, ParsePosition position) { 497 Objects.requireNonNull(text, "text"); 498 Objects.requireNonNull(position, "position"); 499 DateTimeParseContext context = new DateTimeParseContext(this); 500 int pos = position.getIndex(); 501 pos = printerParser.parse(context, text, pos); 502 if (pos < 0) { 503 position.setErrorIndex(~pos); 504 return null; 505 } 506 position.setIndex(pos); 507 return context.toBuilder(); 508 } 509 510 //----------------------------------------------------------------------- 511 /** 512 * Returns the formatter as a composite printer parser. 513 * 514 * @param optional whether the printer/parser should be optional 515 * @return the printer/parser, not null 516 */ 517 CompositePrinterParser toPrinterParser(boolean optional) { 518 return printerParser.withOptional(optional); 519 } 520 521 /** 522 * Returns this formatter as a {@code java.text.Format} instance. 523 * <p> 524 * The returned {@link Format} instance will print any {@link java.time.temporal.TemporalAccessor} 525 * and parses to a resolved {@link DateTimeBuilder}. 526 * <p> 527 * Exceptions will follow the definitions of {@code Format}, see those methods 528 * for details about {@code IllegalArgumentException} during formatting and 529 * {@code ParseException} or null during parsing. 530 * The format does not support attributing of the returned format string. 531 * 532 * @return this formatter as a classic format instance, not null 533 */ 534 public Format toFormat() { 535 return new ClassicFormat(this, null); 536 } 537 538 /** 539 * Returns this formatter as a {@code java.text.Format} instance that will 540 * parse using the specified query. 541 * <p> 542 * The returned {@link Format} instance will print any {@link java.time.temporal.TemporalAccessor} 543 * and parses to the type specified. 544 * The type must be one that is supported by {@link #parse}. 545 * <p> 546 * Exceptions will follow the definitions of {@code Format}, see those methods 547 * for details about {@code IllegalArgumentException} during formatting and 548 * {@code ParseException} or null during parsing. 549 * The format does not support attributing of the returned format string. 550 * 551 * @param parseQuery the query defining the type to parse to, not null 552 * @return this formatter as a classic format instance, not null 553 */ 554 public Format toFormat(TemporalQuery<?> parseQuery) { 555 Objects.requireNonNull(parseQuery, "parseQuery"); 556 return new ClassicFormat(this, parseQuery); 557 } 558 559 //----------------------------------------------------------------------- 560 /** 561 * Returns a description of the underlying formatters. 562 * 563 * @return a description of this formatter, not null 564 */ 565 @Override 566 public String toString() { 567 String pattern = printerParser.toString(); 568 pattern = pattern.startsWith("[") ? pattern : pattern.substring(1, pattern.length() - 1); 569 return pattern; 570 // TODO: Fix tests to not depend on toString() 571 // return "DateTimeFormatter[" + locale + 572 // (chrono != null ? "," + chrono : "") + 573 // (zone != null ? "," + zone : "") + 574 // pattern + "]"; 575 } 576 577 //----------------------------------------------------------------------- 578 /** 579 * Implements the classic Java Format API. 580 * @serial exclude 581 */ 582 @SuppressWarnings("serial") // not actually serializable 583 static class ClassicFormat extends Format { 584 /** The formatter. */ 585 private final DateTimeFormatter formatter; 586 /** The type to be parsed. */ 587 private final TemporalQuery<?> parseType; 588 /** Constructor. */ 589 public ClassicFormat(DateTimeFormatter formatter, TemporalQuery<?> parseType) { 590 this.formatter = formatter; 591 this.parseType = parseType; 592 } 593 594 @Override 595 public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { 596 Objects.requireNonNull(obj, "obj"); 597 Objects.requireNonNull(toAppendTo, "toAppendTo"); 598 Objects.requireNonNull(pos, "pos"); 599 if (obj instanceof TemporalAccessor == false) { 600 throw new IllegalArgumentException("Format target must implement TemporalAccessor"); 601 } 602 pos.setBeginIndex(0); 603 pos.setEndIndex(0); 604 try { 605 formatter.printTo((TemporalAccessor) obj, toAppendTo); 606 } catch (RuntimeException ex) { 607 throw new IllegalArgumentException(ex.getMessage(), ex); 608 } 609 return toAppendTo; 610 } 611 @Override 612 public Object parseObject(String text) throws ParseException { 613 Objects.requireNonNull(text, "text"); 614 try { 615 if (parseType != null) { 616 return formatter.parse(text, parseType); 617 } 618 return formatter.parseToBuilder(text); 619 } catch (DateTimeParseException ex) { 620 throw new ParseException(ex.getMessage(), ex.getErrorIndex()); 621 } catch (RuntimeException ex) { 622 throw (ParseException) new ParseException(ex.getMessage(), 0).initCause(ex); 623 } 624 } 625 @Override 626 public Object parseObject(String text, ParsePosition pos) { 627 Objects.requireNonNull(text, "text"); 628 DateTimeBuilder builder; 629 try { 630 builder = formatter.parseToBuilder(text, pos); 631 } catch (IndexOutOfBoundsException ex) { 632 if (pos.getErrorIndex() < 0) { 633 pos.setErrorIndex(0); 634 } 635 return null; 636 } 637 if (builder == null) { 638 if (pos.getErrorIndex() < 0) { 639 pos.setErrorIndex(0); 640 } 641 return null; 642 } 643 if (parseType == null) { 644 return builder; 645 } 646 try { 647 return builder.resolve().query(parseType); 648 } catch (RuntimeException ex) { 649 pos.setErrorIndex(0); 650 return null; 651 } 652 } 653 } 654 655 }