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 }