001/*
002 * Copyright (c) 2007-2013, Stephen Colebourne & Michael Nascimento Santos
003 *
004 * All rights reserved.
005 *
006 * Redistribution and use in source and binary forms, with or without
007 * modification, are permitted provided that the following conditions are met:
008 *
009 *  * Redistributions of source code must retain the above copyright notice,
010 *    this list of conditions and the following disclaimer.
011 *
012 *  * Redistributions in binary form must reproduce the above copyright notice,
013 *    this list of conditions and the following disclaimer in the documentation
014 *    and/or other materials provided with the distribution.
015 *
016 *  * Neither the name of JSR-310 nor the names of its contributors
017 *    may be used to endorse or promote products derived from this software
018 *    without specific prior written permission.
019 *
020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
021 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
022 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
023 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
024 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
027 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
028 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
029 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
030 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031 */
032package org.threeten.bp.format;
033
034import java.io.IOException;
035import java.text.FieldPosition;
036import java.text.Format;
037import java.text.ParseException;
038import java.text.ParsePosition;
039import java.util.Arrays;
040import java.util.Locale;
041import java.util.Objects;
042
043import org.threeten.bp.DateTimeException;
044import org.threeten.bp.ZoneId;
045import org.threeten.bp.format.DateTimeFormatterBuilder.CompositePrinterParser;
046import org.threeten.bp.temporal.Chrono;
047import org.threeten.bp.temporal.ChronoField;
048import org.threeten.bp.temporal.TemporalAccessor;
049
050/**
051 * Formatter for printing and parsing date-time objects.
052 * <p>
053 * This class provides the main application entry point for printing and parsing.
054 * Common instances of {@code DateTimeFormatter} are provided by {@link DateTimeFormatters}.
055 * For more complex formatters, a {@link DateTimeFormatterBuilder builder} is provided.
056 * <p>
057 * In most cases, it is not necessary to use this class directly when formatting.
058 * The main date-time classes provide two methods - one for printing,
059 * {@code toString(DateTimeFormatter formatter)}, and one for parsing,
060 * {@code parse(CharSequence text, DateTimeFormatter formatter)}.
061 * For example:
062 * <pre>
063 *  String text = date.toString(formatter);
064 *  LocalDate date = LocalDate.parse(text, formatter);
065 * </pre>
066 * Some aspects of printing and parsing are dependent on the locale.
067 * The locale can be changed using the {@link #withLocale(Locale)} method
068 * which returns a new formatter in the requested locale.
069 * <p>
070 * Some applications may need to use the older {@link Format} class for formatting.
071 * The {@link #toFormat()} method returns an implementation of the old API.
072 *
073 * <h3>Specification for implementors</h3>
074 * This class is immutable and thread-safe.
075 */
076public final class DateTimeFormatter {
077
078    /**
079     * The printer and/or parser to use, not null.
080     */
081    private final CompositePrinterParser printerParser;
082    /**
083     * The locale to use for formatting, not null.
084     */
085    private final Locale locale;
086    /**
087     * The symbols to use for formatting, not null.
088     */
089    private final DateTimeFormatSymbols symbols;
090    /**
091     * The chronology to use for formatting, null for no override.
092     */
093    private final Chrono<?> chrono;
094    /**
095     * The zone to use for formatting, null for no override.
096     */
097    private final ZoneId zone;
098
099    /**
100     * Constructor.
101     *
102     * @param printerParser  the printer/parser to use, not null
103     * @param locale  the locale to use, not null
104     * @param symbols  the symbols to use, not null
105     * @param chrono  the chronology to use, null for no override
106     * @param zone  the zone to use, null for no override
107     */
108    DateTimeFormatter(CompositePrinterParser printerParser, Locale locale,
109                      DateTimeFormatSymbols symbols, Chrono<?> chrono, ZoneId zone) {
110        this.printerParser = Objects.requireNonNull(printerParser, "printerParser");
111        this.locale = Objects.requireNonNull(locale, "locale");
112        this.symbols = Objects.requireNonNull(symbols, "symbols");
113        this.chrono = chrono;
114        this.zone = zone;
115    }
116
117    //-----------------------------------------------------------------------
118    /**
119     * Gets the locale to be used during formatting.
120     * <p>
121     * This is used to lookup any part of the formatter needing specific
122     * localization, such as the text or localized pattern.
123     *
124     * @return the locale of this formatter, not null
125     */
126    public Locale getLocale() {
127        return locale;
128    }
129
130    /**
131     * Returns a copy of this formatter with a new locale.
132     * <p>
133     * This is used to lookup any part of the formatter needing specific
134     * localization, such as the text or localized pattern.
135     * <p>
136     * This instance is immutable and unaffected by this method call.
137     *
138     * @param locale  the new locale, not null
139     * @return a formatter based on this formatter with the requested locale, not null
140     */
141    public DateTimeFormatter withLocale(Locale locale) {
142        if (this.locale.equals(locale)) {
143            return this;
144        }
145        return new DateTimeFormatter(printerParser, locale, symbols, chrono, zone);
146    }
147
148    //-----------------------------------------------------------------------
149    /**
150     * Gets the set of symbols to be used during formatting.
151     *
152     * @return the locale of this formatter, not null
153     */
154    public DateTimeFormatSymbols getSymbols() {
155        return symbols;
156    }
157
158    /**
159     * Returns a copy of this formatter with a new set of symbols.
160     * <p>
161     * This instance is immutable and unaffected by this method call.
162     *
163     * @param symbols  the new symbols, not null
164     * @return a formatter based on this formatter with the requested symbols, not null
165     */
166    public DateTimeFormatter withSymbols(DateTimeFormatSymbols symbols) {
167        if (this.symbols.equals(symbols)) {
168            return this;
169        }
170        return new DateTimeFormatter(printerParser, locale, symbols, chrono, zone);
171    }
172
173    //-----------------------------------------------------------------------
174    /**
175     * Gets the overriding chronology to be used during formatting.
176     * <p>
177     * This returns the override chronology, used to convert dates.
178     * By default, a formatter has no override chronology, returning null.
179     * See {@link #withChrono(Chrono)} for more details on overriding.
180     *
181     * @return the chronology of this formatter, null if no override
182     */
183    public Chrono<?> getChrono() {
184        return chrono;
185    }
186
187    /**
188     * Returns a copy of this formatter with a new override chronology.
189     * <p>
190     * This returns a formatter with similar state to this formatter but
191     * with the override chronology set.
192     * By default, a formatter has no override chronology, returning null.
193     * <p>
194     * If an override is added, then any date that is printed or parsed will be affected.
195     * <p>
196     * When printing, if the {@code Temporal} object contains a date then it will
197     * be converted to a date in the override chronology.
198     * Any time or zone will be retained unless overridden.
199     * The converted result will behave in a manner equivalent to an implementation
200     * of {@code ChronoLocalDate},{@code ChronoLocalDateTime} or {@code ChronoZonedDateTime}.
201     * <p>
202     * When parsing, the override chronology will be used to interpret the
203     * {@linkplain ChronoField fields} into a date unless the
204     * formatter directly parses a valid chronology.
205     * <p>
206     * This instance is immutable and unaffected by this method call.
207     *
208     * @param chrono  the new chronology, not null
209     * @return a formatter based on this formatter with the requested override chronology, not null
210     */
211    public DateTimeFormatter withChrono(Chrono<?> chrono) {
212        if (Objects.equals(this.chrono, chrono)) {
213            return this;
214        }
215        return new DateTimeFormatter(printerParser, locale, symbols, chrono, zone);
216    }
217
218    //-----------------------------------------------------------------------
219    /**
220     * Gets the overriding zone to be used during formatting.
221     * <p>
222     * This returns the override zone, used to convert instants.
223     * By default, a formatter has no override zone, returning null.
224     * See {@link #withZone(ZoneId)} for more details on overriding.
225     *
226     * @return the chronology of this formatter, null if no override
227     */
228    public ZoneId getZone() {
229        return zone;
230    }
231
232    /**
233     * Returns a copy of this formatter with a new override zone.
234     * <p>
235     * This returns a formatter with similar state to this formatter but
236     * with the override zone set.
237     * By default, a formatter has no override zone, returning null.
238     * <p>
239     * If an override is added, then any instant that is printed or parsed will be affected.
240     * <p>
241     * When printing, if the {@code Temporal} object contains an instant then it will
242     * be converted to a zoned date-time using the override zone.
243     * If the input has a chronology then it will be retained unless overridden.
244     * If the input does not have a chronology, such as {@code Instant}, then
245     * the ISO chronology will be used.
246     * The converted result will behave in a manner equivalent to an implementation
247     * of {@code ChronoZonedDateTime}.
248     * <p>
249     * When parsing, the override zone will be used to interpret the
250     * {@linkplain ChronoField fields} into an instant unless the
251     * formatter directly parses a valid zone.
252     * <p>
253     * This instance is immutable and unaffected by this method call.
254     *
255     * @param zone  the new override zone, not null
256     * @return a formatter based on this formatter with the requested override zone, not null
257     */
258    public DateTimeFormatter withZone(ZoneId zone) {
259        if (Objects.equals(this.zone, zone)) {
260            return this;
261        }
262        return new DateTimeFormatter(printerParser, locale, symbols, chrono, zone);
263    }
264
265    //-----------------------------------------------------------------------
266    /**
267     * Prints a date-time object using this formatter.
268     * <p>
269     * This prints the date-time to a String using the rules of the formatter.
270     *
271     * @param temporal  the temporal object to print, not null
272     * @return the printed string, not null
273     * @throws DateTimeException if an error occurs during printing
274     */
275    public String print(TemporalAccessor temporal) {
276        StringBuilder buf = new StringBuilder(32);
277        printTo(temporal, buf);
278        return buf.toString();
279    }
280
281    //-----------------------------------------------------------------------
282    /**
283     * Prints a date-time object to an {@code Appendable} using this formatter.
284     * <p>
285     * This prints the date-time to the specified destination.
286     * {@link Appendable} is a general purpose interface that is implemented by all
287     * key character output classes including {@code StringBuffer}, {@code StringBuilder},
288     * {@code PrintStream} and {@code Writer}.
289     * <p>
290     * Although {@code Appendable} methods throw an {@code IOException}, this method does not.
291     * Instead, any {@code IOException} is wrapped in a runtime exception.
292     * See {@link DateTimePrintException#rethrowIOException()} for a means
293     * to extract the {@code IOException}.
294     *
295     * @param temporal  the temporal object to print, not null
296     * @param appendable  the appendable to print to, not null
297     * @throws DateTimeException if an error occurs during printing
298     */
299    public void printTo(TemporalAccessor temporal, Appendable appendable) {
300        Objects.requireNonNull(temporal, "temporal");
301        Objects.requireNonNull(appendable, "appendable");
302        try {
303            DateTimePrintContext context = new DateTimePrintContext(temporal, this);
304            if (appendable instanceof StringBuilder) {
305                printerParser.print(context, (StringBuilder) appendable);
306            } else {
307                // buffer output to avoid writing to appendable in case of error
308                StringBuilder buf = new StringBuilder(32);
309                printerParser.print(context, buf);
310                appendable.append(buf);
311            }
312        } catch (IOException ex) {
313            throw new DateTimePrintException(ex.getMessage(), ex);
314        }
315    }
316
317    //-----------------------------------------------------------------------
318    /**
319     * Fully parses the text producing an object of the specified type.
320     * <p>
321     * Most applications should use this method for parsing.
322     * It parses the entire text to produce the required date-time.
323     * For example:
324     * <pre>
325     *  LocalDateTime dt = parser.parse(str, LocalDateTime.class);
326     * </pre>
327     * If the parse completes without reading the entire length of the text,
328     * or a problem occurs during parsing or merging, then an exception is thrown.
329     *
330     * @param <T> the type to extract
331     * @param text  the text to parse, not null
332     * @param type  the type to extract, not null
333     * @return the parsed date-time, not null
334     * @throws DateTimeParseException if the parse fails
335     */
336    public <T> T parse(CharSequence text, Class<T> type) {
337        Objects.requireNonNull(text, "text");
338        Objects.requireNonNull(type, "type");
339        String str = text.toString();  // parsing whole String, so this makes sense
340        try {
341            DateTimeBuilder builder = parseToBuilder(str).resolve();
342            return builder.build(type);
343        } catch (DateTimeParseException ex) {
344            throw ex;
345        } catch (RuntimeException ex) {
346            throw createError(str, ex);
347        }
348    }
349
350    /**
351     * Fully parses the text producing an object of one of the specified types.
352     * <p>
353     * This parse method is convenient for use when the parser can handle optional elements.
354     * For example, a pattern of 'yyyy-MM[-dd[Z]]' can be fully parsed to an {@code OffsetDate},
355     * or partially parsed to a {@code LocalDate} or a {@code YearMonth}.
356     * The types must be specified in order, starting from the best matching full-parse option
357     * and ending with the worst matching minimal parse option.
358     * <p>
359     * The result is associated with the first type that successfully parses.
360     * Normally, applications will use {@code instanceof} to check the result.
361     * For example:
362     * <pre>
363     *  TemporalAccessor dt = parser.parseBest(str, OffsetDate.class, LocalDate.class);
364     *  if (dt instanceof OffsetDate) {
365     *   ...
366     *  } else {
367     *   ...
368     *  }
369     * </pre>
370     * If the parse completes without reading the entire length of the text,
371     * or a problem occurs during parsing or merging, then an exception is thrown.
372     *
373     * @param text  the text to parse, not null
374     * @param types  the types to attempt to parse to, which must implement {@code TemporalAccessor}, not null
375     * @return the parsed date-time, not null
376     * @throws IllegalArgumentException if less than 2 types are specified
377     * @throws DateTimeParseException if the parse fails
378     */
379    public TemporalAccessor parseBest(CharSequence text, Class<?>... types) {
380        Objects.requireNonNull(text, "text");
381        Objects.requireNonNull(types, "types");
382        if (types.length < 2) {
383            throw new IllegalArgumentException("At least two types must be specified");
384        }
385        String str = text.toString();  // parsing whole String, so this makes sense
386        try {
387            DateTimeBuilder builder = parseToBuilder(str).resolve();
388            for (Class<?> type : types) {
389                try {
390                    return (TemporalAccessor) builder.build(type);
391                } catch (RuntimeException ex) {
392                    // continue
393                }
394            }
395            throw new DateTimeException("Unable to convert parsed text to any specified type: " + Arrays.toString(types));
396        } catch (DateTimeParseException ex) {
397            throw ex;
398        } catch (RuntimeException ex) {
399            throw createError(str, ex);
400        }
401    }
402
403    private DateTimeParseException createError(String str, RuntimeException ex) {
404        String abbr = str;
405        if (abbr.length() > 64) {
406            abbr = abbr.substring(0, 64) + "...";
407        }
408        return new DateTimeParseException("Text '" + abbr + "' could not be parsed: " + ex.getMessage(), str, 0, ex);
409    }
410
411    //-----------------------------------------------------------------------
412    /**
413     * Parses the text to a builder.
414     * <p>
415     * This parses to a {@code DateTimeBuilder} ensuring that the text is fully parsed.
416     * This method throws {@link DateTimeParseException} if unable to parse, or
417     * some other {@code DateTimeException} if another date/time problem occurs.
418     *
419     * @param text  the text to parse, not null
420     * @return the engine representing the result of the parse, not null
421     * @throws DateTimeParseException if the parse fails
422     */
423    public DateTimeBuilder parseToBuilder(CharSequence text) {
424        Objects.requireNonNull(text, "text");
425        String str = text.toString();  // parsing whole String, so this makes sense
426        ParsePosition pos = new ParsePosition(0);
427        DateTimeBuilder result = parseToBuilder(str, pos);
428        if (result == null || pos.getErrorIndex() >= 0 || pos.getIndex() < str.length()) {
429            String abbr = str;
430            if (abbr.length() > 64) {
431                abbr = abbr.substring(0, 64) + "...";
432            }
433            if (pos.getErrorIndex() >= 0) {
434                throw new DateTimeParseException("Text '" + abbr + "' could not be parsed at index " +
435                        pos.getErrorIndex(), str, pos.getErrorIndex());
436            } else {
437                throw new DateTimeParseException("Text '" + abbr + "' could not be parsed, unparsed text found at index " +
438                        pos.getIndex(), str, pos.getIndex());
439            }
440        }
441        return result;
442    }
443
444    /**
445     * Parses the text to a builder.
446     * <p>
447     * This parses to a {@code DateTimeBuilder} but does not require the input to be fully parsed.
448     * <p>
449     * This method does not throw {@link DateTimeParseException}.
450     * Instead, errors are returned within the state of the specified parse position.
451     * Callers must check for errors before using the context.
452     * <p>
453     * This method may throw some other {@code DateTimeException} if a date/time problem occurs.
454     *
455     * @param text  the text to parse, not null
456     * @param position  the position to parse from, updated with length parsed
457     *  and the index of any error, not null
458     * @return the parsed text, null only if the parse results in an error
459     * @throws IndexOutOfBoundsException if the position is invalid
460     */
461    public DateTimeBuilder parseToBuilder(CharSequence text, ParsePosition position) {
462        Objects.requireNonNull(text, "text");
463        Objects.requireNonNull(position, "position");
464        DateTimeParseContext context = new DateTimeParseContext(this);
465        int pos = position.getIndex();
466        pos = printerParser.parse(context, text, pos);
467        if (pos < 0) {
468            position.setErrorIndex(~pos);
469            return null;
470        }
471        position.setIndex(pos);
472        return context.toBuilder();
473    }
474
475    //-----------------------------------------------------------------------
476    /**
477     * Returns the formatter as a composite printer parser.
478     *
479     * @param optional  whether the printer/parser should be optional
480     * @return the printer/parser, not null
481     */
482    CompositePrinterParser toPrinterParser(boolean optional) {
483        return printerParser.withOptional(optional);
484    }
485
486    /**
487     * Returns this formatter as a {@code java.text.Format} instance.
488     * <p>
489     * The returned {@link Format} instance will print any {@link TemporalAccessor}
490     * and parses to a resolved {@link DateTimeBuilder}.
491     * <p>
492     * Exceptions will follow the definitions of {@code Format}, see those methods
493     * for details about {@code IllegalArgumentException} during formatting and
494     * {@code ParseException} or null during parsing.
495     * The format does not support attributing of the returned format string.
496     *
497     * @return this formatter as a classic format instance, not null
498     */
499    public Format toFormat() {
500        return new ClassicFormat(this, null);
501    }
502
503    /**
504     * Returns this formatter as a {@code java.text.Format} instance that will
505     * parse to the specified type.
506     * <p>
507     * The returned {@link Format} instance will print any {@link TemporalAccessor}
508     * and parses to the type specified.
509     * The type must be one that is supported by {@link #parse}.
510     * <p>
511     * Exceptions will follow the definitions of {@code Format}, see those methods
512     * for details about {@code IllegalArgumentException} during formatting and
513     * {@code ParseException} or null during parsing.
514     * The format does not support attributing of the returned format string.
515     *
516     * @param parseType  the type to parse to, not null
517     * @return this formatter as a classic format instance, not null
518     */
519    public Format toFormat(Class<?> parseType) {
520        Objects.requireNonNull(parseType, "parseType");
521        return new ClassicFormat(this, parseType);
522    }
523
524    //-----------------------------------------------------------------------
525    /**
526     * Returns a description of the underlying formatters.
527     *
528     * @return a description of this formatter, not null
529     */
530    @Override
531    public String toString() {
532        String pattern = printerParser.toString();
533        return pattern.startsWith("[") ? pattern : pattern.substring(1, pattern.length() - 1);
534    }
535
536    //-----------------------------------------------------------------------
537    /**
538     * Implements the classic Java Format API.
539     * @serial exclude
540     */
541    @SuppressWarnings("serial")  // not actually serializable
542    static class ClassicFormat extends Format {
543        /** The formatter. */
544        private final DateTimeFormatter formatter;
545        /** The type to be parsed. */
546        private final Class<?> parseType;
547        /** Constructor. */
548        public ClassicFormat(DateTimeFormatter formatter, Class<?> parseType) {
549            this.formatter = formatter;
550            this.parseType = parseType;
551        }
552
553        @Override
554        public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
555            Objects.requireNonNull(obj, "obj");
556            Objects.requireNonNull(toAppendTo, "toAppendTo");
557            Objects.requireNonNull(pos, "pos");
558            if (obj instanceof TemporalAccessor == false) {
559                throw new IllegalArgumentException("Format target must implement TemporalAccessor");
560            }
561            pos.setBeginIndex(0);
562            pos.setEndIndex(0);
563            try {
564                formatter.printTo((TemporalAccessor) obj, toAppendTo);
565            } catch (RuntimeException ex) {
566                throw new IllegalArgumentException(ex.getMessage(), ex);
567            }
568            return toAppendTo;
569        }
570        @Override
571        public Object parseObject(String text) throws ParseException {
572            Objects.requireNonNull(text, "text");
573            try {
574                if (parseType != null) {
575                    return formatter.parse(text, parseType);
576                }
577                return formatter.parseToBuilder(text);
578            } catch (DateTimeParseException ex) {
579                throw new ParseException(ex.getMessage(), ex.getErrorIndex());
580            } catch (RuntimeException ex) {
581                throw (ParseException) new ParseException(ex.getMessage(), 0).initCause(ex);
582            }
583        }
584        @Override
585        public Object parseObject(String text, ParsePosition pos) {
586            Objects.requireNonNull(text, "text");
587            DateTimeBuilder builder;
588            try {
589                builder = formatter.parseToBuilder(text, pos);
590            } catch (IndexOutOfBoundsException ex) {
591                if (pos.getErrorIndex() < 0) {
592                    pos.setErrorIndex(0);
593                }
594                return null;
595            }
596            if (builder == null) {
597                if (pos.getErrorIndex() < 0) {
598                    pos.setErrorIndex(0);
599                }
600                return null;
601            }
602            if (parseType == null) {
603                return builder;
604            }
605            try {
606                return builder.resolve().build(parseType);
607            } catch (RuntimeException ex) {
608                pos.setErrorIndex(0);
609                return null;
610            }
611        }
612    }
613
614}