All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.threeten.bp.format.DateTimeFormatter Maven / Gradle / Ivy

Go to download

Backport of JSR-310 from JDK 8 to JDK 7 and JDK 6. NOT an implementation of the JSR.

The newest version!
/*
 * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 *  * Neither the name of JSR-310 nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.threeten.bp.format;

import static org.threeten.bp.temporal.ChronoField.DAY_OF_MONTH;
import static org.threeten.bp.temporal.ChronoField.DAY_OF_WEEK;
import static org.threeten.bp.temporal.ChronoField.DAY_OF_YEAR;
import static org.threeten.bp.temporal.ChronoField.HOUR_OF_DAY;
import static org.threeten.bp.temporal.ChronoField.MINUTE_OF_HOUR;
import static org.threeten.bp.temporal.ChronoField.MONTH_OF_YEAR;
import static org.threeten.bp.temporal.ChronoField.NANO_OF_SECOND;
import static org.threeten.bp.temporal.ChronoField.SECOND_OF_MINUTE;
import static org.threeten.bp.temporal.ChronoField.YEAR;

import java.io.IOException;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import org.threeten.bp.DateTimeException;
import org.threeten.bp.Period;
import org.threeten.bp.ZoneId;
import org.threeten.bp.ZoneOffset;
import org.threeten.bp.chrono.Chronology;
import org.threeten.bp.chrono.IsoChronology;
import org.threeten.bp.format.DateTimeFormatterBuilder.CompositePrinterParser;
import org.threeten.bp.format.DateTimeParseContext.Parsed;
import org.threeten.bp.jdk8.Jdk8Methods;
import org.threeten.bp.temporal.ChronoField;
import org.threeten.bp.temporal.IsoFields;
import org.threeten.bp.temporal.TemporalAccessor;
import org.threeten.bp.temporal.TemporalField;
import org.threeten.bp.temporal.TemporalQuery;

/**
 * Formatter for printing and parsing date-time objects.
 * 

* This class provides the main application entry point for printing and parsing. * Common instances of {@code DateTimeFormatter} are provided: *

    *
  • Using pattern letters, such as {@code yyyy-MMM-dd} *
  • Using localized styles, such as {@code long} or {@code medium} *
  • Using predefined constants, such as {@link #ISO_LOCAL_DATE} *

*

* For more complex formatters, a {@link DateTimeFormatterBuilder builder} is provided. *

* In most cases, it is not necessary to use this class directly when formatting. * The main date-time classes provide two methods - one for formatting, * {@code format(DateTimeFormatter formatter)}, and one for parsing, * For example: *

 *  String text = date.format(formatter);
 *  LocalDate date = LocalDate.parse(text, formatter);
 * 
* Some aspects of printing and parsing are dependent on the locale. * The locale can be changed using the {@link #withLocale(Locale)} method * which returns a new formatter in the requested locale. *

* Some applications may need to use the older {@link Format} class for formatting. * The {@link #toFormat()} method returns an implementation of the old API. * *

Specification for implementors

* This class is immutable and thread-safe. */ public final class DateTimeFormatter { //----------------------------------------------------------------------- /** * Returns the ISO date formatter that prints/parses a date without an offset, * such as '2011-12-03'. *

* This returns an immutable formatter capable of printing and parsing * the ISO-8601 extended local date format. * The format consists of: *

    *
  • Four digits or more for the {@link ChronoField#YEAR year}. * Years in the range 0000 to 9999 will be pre-padded by zero to ensure four digits. * Years outside that range will have a prefixed positive or negative symbol. *
  • A dash *
  • Two digits for the {@link ChronoField#MONTH_OF_YEAR month-of-year}. * This is pre-padded by zero to ensure two digits. *
  • A dash *
  • Two digits for the {@link ChronoField#DAY_OF_MONTH day-of-month}. * This is pre-padded by zero to ensure two digits. *

*/ public static final DateTimeFormatter ISO_LOCAL_DATE; static { ISO_LOCAL_DATE = new DateTimeFormatterBuilder() .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD) .appendLiteral('-') .appendValue(MONTH_OF_YEAR, 2) .appendLiteral('-') .appendValue(DAY_OF_MONTH, 2) .toFormatter(ResolverStyle.STRICT).withChronology(IsoChronology.INSTANCE); } //----------------------------------------------------------------------- /** * Returns the ISO date formatter that prints/parses a date with an offset, * such as '2011-12-03+01:00'. *

* This returns an immutable formatter capable of printing and parsing * the ISO-8601 extended offset date format. * The format consists of: *

    *
  • The {@link #ISO_LOCAL_DATE} *
  • The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then * they will be handled even though this is not part of the ISO-8601 standard. * Parsing is case insensitive. *

*/ public static final DateTimeFormatter ISO_OFFSET_DATE; static { ISO_OFFSET_DATE = new DateTimeFormatterBuilder() .parseCaseInsensitive() .append(ISO_LOCAL_DATE) .appendOffsetId() .toFormatter(ResolverStyle.STRICT).withChronology(IsoChronology.INSTANCE); } //----------------------------------------------------------------------- /** * Returns the ISO date formatter that prints/parses a date with the * offset if available, such as '2011-12-03' or '2011-12-03+01:00'. *

* This returns an immutable formatter capable of printing and parsing * the ISO-8601 extended date format. * The format consists of: *

    *
  • The {@link #ISO_LOCAL_DATE} *
  • If the offset is not available to print/parse then the format is complete. *
  • The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then * they will be handled even though this is not part of the ISO-8601 standard. * Parsing is case insensitive. *

* As this formatter has an optional element, it may be necessary to parse using * {@link DateTimeFormatter#parseBest}. */ public static final DateTimeFormatter ISO_DATE; static { ISO_DATE = new DateTimeFormatterBuilder() .parseCaseInsensitive() .append(ISO_LOCAL_DATE) .optionalStart() .appendOffsetId() .toFormatter(ResolverStyle.STRICT).withChronology(IsoChronology.INSTANCE); } //----------------------------------------------------------------------- /** * Returns the ISO time formatter that prints/parses a time without an offset, * such as '10:15' or '10:15:30'. *

* This returns an immutable formatter capable of printing and parsing * the ISO-8601 extended local time format. * The format consists of: *

    *
  • Two digits for the {@link ChronoField#HOUR_OF_DAY hour-of-day}. * This is pre-padded by zero to ensure two digits. *
  • A colon *
  • Two digits for the {@link ChronoField#MINUTE_OF_HOUR minute-of-hour}. * This is pre-padded by zero to ensure two digits. *
  • If the second-of-minute is not available to print/parse then the format is complete. *
  • A colon *
  • Two digits for the {@link ChronoField#SECOND_OF_MINUTE second-of-minute}. * This is pre-padded by zero to ensure two digits. *
  • If the nano-of-second is zero or not available to print/parse then the format is complete. *
  • A decimal point *
  • One to nine digits for the {@link ChronoField#NANO_OF_SECOND nano-of-second}. * As many digits will be printed as required. *

*/ public static final DateTimeFormatter ISO_LOCAL_TIME; static { ISO_LOCAL_TIME = new DateTimeFormatterBuilder() .appendValue(HOUR_OF_DAY, 2) .appendLiteral(':') .appendValue(MINUTE_OF_HOUR, 2) .optionalStart() .appendLiteral(':') .appendValue(SECOND_OF_MINUTE, 2) .optionalStart() .appendFraction(NANO_OF_SECOND, 0, 9, true) .toFormatter(ResolverStyle.STRICT); } //----------------------------------------------------------------------- /** * Returns the ISO time formatter that prints/parses a time with an offset, * such as '10:15+01:00' or '10:15:30+01:00'. *

* This returns an immutable formatter capable of printing and parsing * the ISO-8601 extended offset time format. * The format consists of: *

    *
  • The {@link #ISO_LOCAL_TIME} *
  • The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then * they will be handled even though this is not part of the ISO-8601 standard. * Parsing is case insensitive. *

*/ public static final DateTimeFormatter ISO_OFFSET_TIME; static { ISO_OFFSET_TIME = new DateTimeFormatterBuilder() .parseCaseInsensitive() .append(ISO_LOCAL_TIME) .appendOffsetId() .toFormatter(ResolverStyle.STRICT); } //----------------------------------------------------------------------- /** * Returns the ISO time formatter that prints/parses a time, with the * offset if available, such as '10:15', '10:15:30' or '10:15:30+01:00'. *

* This returns an immutable formatter capable of printing and parsing * the ISO-8601 extended offset time format. * The format consists of: *

    *
  • The {@link #ISO_LOCAL_TIME} *
  • If the offset is not available to print/parse then the format is complete. *
  • The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then * they will be handled even though this is not part of the ISO-8601 standard. * Parsing is case insensitive. *

* As this formatter has an optional element, it may be necessary to parse using * {@link DateTimeFormatter#parseBest}. */ public static final DateTimeFormatter ISO_TIME; static { ISO_TIME = new DateTimeFormatterBuilder() .parseCaseInsensitive() .append(ISO_LOCAL_TIME) .optionalStart() .appendOffsetId() .toFormatter(ResolverStyle.STRICT); } //----------------------------------------------------------------------- /** * Returns the ISO date formatter that prints/parses a date-time * without an offset, such as '2011-12-03T10:15:30'. *

* This returns an immutable formatter capable of printing and parsing * the ISO-8601 extended offset date-time format. * The format consists of: *

    *
  • The {@link #ISO_LOCAL_DATE} *
  • The letter 'T'. Parsing is case insensitive. *
  • The {@link #ISO_LOCAL_TIME} *

*/ public static final DateTimeFormatter ISO_LOCAL_DATE_TIME; static { ISO_LOCAL_DATE_TIME = new DateTimeFormatterBuilder() .parseCaseInsensitive() .append(ISO_LOCAL_DATE) .appendLiteral('T') .append(ISO_LOCAL_TIME) .toFormatter(ResolverStyle.STRICT).withChronology(IsoChronology.INSTANCE); } //----------------------------------------------------------------------- /** * Returns the ISO date formatter that prints/parses a date-time * with an offset, such as '2011-12-03T10:15:30+01:00'. *

* This returns an immutable formatter capable of printing and parsing * the ISO-8601 extended offset date-time format. * The format consists of: *

    *
  • The {@link #ISO_LOCAL_DATE_TIME} *
  • The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then * they will be handled even though this is not part of the ISO-8601 standard. * Parsing is case insensitive. *

*/ public static final DateTimeFormatter ISO_OFFSET_DATE_TIME; static { ISO_OFFSET_DATE_TIME = new DateTimeFormatterBuilder() .parseCaseInsensitive() .append(ISO_LOCAL_DATE_TIME) .appendOffsetId() .toFormatter(ResolverStyle.STRICT).withChronology(IsoChronology.INSTANCE); } //----------------------------------------------------------------------- /** * Returns the ISO date formatter that prints/parses a date-time with * offset and zone, such as '2011-12-03T10:15:30+01:00[Europe/Paris]'. *

* This returns an immutable formatter capable of printing and parsing * a format that extends the ISO-8601 extended offset date-time format * to add the time-zone. * The format consists of: *

    *
  • The {@link #ISO_OFFSET_DATE_TIME} *
  • If the zone ID is not available or is a {@code ZoneOffset} then the format is complete. *
  • An open square bracket '['. *
  • The {@link ZoneId#getId() zone ID}. This is not part of the ISO-8601 standard. * Parsing is case sensitive. *
  • A close square bracket ']'. *

*/ public static final DateTimeFormatter ISO_ZONED_DATE_TIME; static { ISO_ZONED_DATE_TIME = new DateTimeFormatterBuilder() .append(ISO_OFFSET_DATE_TIME) .optionalStart() .appendLiteral('[') .parseCaseSensitive() .appendZoneRegionId() .appendLiteral(']') .toFormatter(ResolverStyle.STRICT).withChronology(IsoChronology.INSTANCE); } //----------------------------------------------------------------------- /** * Returns the ISO date formatter that prints/parses a date-time * with the offset and zone if available, such as '2011-12-03T10:15:30', * '2011-12-03T10:15:30+01:00' or '2011-12-03T10:15:30+01:00[Europe/Paris]'. *

* This returns an immutable formatter capable of printing and parsing * the ISO-8601 extended offset date-time format. * The format consists of: *

    *
  • The {@link #ISO_LOCAL_DATE_TIME} *
  • If the offset is not available to print/parse then the format is complete. *
  • The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then * they will be handled even though this is not part of the ISO-8601 standard. *
  • If the zone ID is not available or is a {@code ZoneOffset} then the format is complete. *
  • An open square bracket '['. *
  • The {@link ZoneId#getId() zone ID}. This is not part of the ISO-8601 standard. * Parsing is case sensitive. *
  • A close square bracket ']'. *

* As this formatter has an optional element, it may be necessary to parse using * {@link DateTimeFormatter#parseBest}. */ public static final DateTimeFormatter ISO_DATE_TIME; static { ISO_DATE_TIME = new DateTimeFormatterBuilder() .append(ISO_LOCAL_DATE_TIME) .optionalStart() .appendOffsetId() .optionalStart() .appendLiteral('[') .parseCaseSensitive() .appendZoneRegionId() .appendLiteral(']') .toFormatter(ResolverStyle.STRICT).withChronology(IsoChronology.INSTANCE); } //----------------------------------------------------------------------- /** * Returns the ISO date formatter that prints/parses the ordinal date * without an offset, such as '2012-337'. *

* This returns an immutable formatter capable of printing and parsing * the ISO-8601 extended ordinal date format. * The format consists of: *

    *
  • Four digits or more for the {@link ChronoField#YEAR year}. * Years in the range 0000 to 9999 will be pre-padded by zero to ensure four digits. * Years outside that range will have a prefixed positive or negative symbol. *
  • A dash *
  • Three digits for the {@link ChronoField#DAY_OF_YEAR day-of-year}. * This is pre-padded by zero to ensure three digits. *
  • If the offset is not available to print/parse then the format is complete. *
  • The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then * they will be handled even though this is not part of the ISO-8601 standard. * Parsing is case insensitive. *

* As this formatter has an optional element, it may be necessary to parse using * {@link DateTimeFormatter#parseBest}. */ public static final DateTimeFormatter ISO_ORDINAL_DATE; static { ISO_ORDINAL_DATE = new DateTimeFormatterBuilder() .parseCaseInsensitive() .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD) .appendLiteral('-') .appendValue(DAY_OF_YEAR, 3) .optionalStart() .appendOffsetId() .toFormatter(ResolverStyle.STRICT).withChronology(IsoChronology.INSTANCE); } //----------------------------------------------------------------------- /** * Returns the ISO date formatter that prints/parses the week-based date * without an offset, such as '2012-W48-6'. *

* This returns an immutable formatter capable of printing and parsing * the ISO-8601 extended week-based date format. * The format consists of: *

    *
  • Four digits or more for the {@link IsoFields#WEEK_BASED_YEAR week-based-year}. * Years in the range 0000 to 9999 will be pre-padded by zero to ensure four digits. * Years outside that range will have a prefixed positive or negative symbol. *
  • A dash *
  • The letter 'W'. Parsing is case insensitive. *
  • Two digits for the {@link IsoFields#WEEK_OF_WEEK_BASED_YEAR week-of-week-based-year}. * This is pre-padded by zero to ensure three digits. *
  • A dash *
  • One digit for the {@link ChronoField#DAY_OF_WEEK day-of-week}. * The value run from Monday (1) to Sunday (7). *
  • If the offset is not available to print/parse then the format is complete. *
  • The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then * they will be handled even though this is not part of the ISO-8601 standard. * Parsing is case insensitive. *

* As this formatter has an optional element, it may be necessary to parse using * {@link DateTimeFormatter#parseBest}. */ public static final DateTimeFormatter ISO_WEEK_DATE; static { ISO_WEEK_DATE = new DateTimeFormatterBuilder() .parseCaseInsensitive() .appendValue(IsoFields.WEEK_BASED_YEAR, 4, 10, SignStyle.EXCEEDS_PAD) .appendLiteral("-W") .appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 2) .appendLiteral('-') .appendValue(DAY_OF_WEEK, 1) .optionalStart() .appendOffsetId() .toFormatter(ResolverStyle.STRICT).withChronology(IsoChronology.INSTANCE); } //----------------------------------------------------------------------- /** * The ISO instant formatter that formats or parses an instant in UTC, * such as '2011-12-03T10:15:30Z'. *

* This returns an immutable formatter capable of formatting and parsing * the ISO-8601 instant format. * When formatting, the second-of-minute is always output. * The nano-of-second outputs zero, three, six or nine digits as necessary. * When parsing, time to at least the seconds field is required. * Fractional seconds from zero to nine are parsed. * The localized decimal style is not used. *

* This is a special case formatter intended to allow a human readable form * of an {@link org.threeten.bp.Instant Instant}. * The {@code Instant} class is designed to * only represent a point in time and internally stores a value in nanoseconds * from a fixed epoch of 1970-01-01Z. As such, an {@code Instant} cannot be * formatted as a date or time without providing some form of time-zone. * This formatter allows the {@code Instant} to be formatted, by providing * a suitable conversion using {@code ZoneOffset.UTC}. *

* The format consists of: *

    *
  • The {@link #ISO_OFFSET_DATE_TIME} where the instant is converted from * {@link ChronoField#INSTANT_SECONDS} and {@link ChronoField#NANO_OF_SECOND} * using the {@code UTC} offset. Parsing is case insensitive. *
*

* The returned formatter has no override chronology or zone. * It uses the {@link ResolverStyle#STRICT STRICT} resolver style. */ public static final DateTimeFormatter ISO_INSTANT; static { ISO_INSTANT = new DateTimeFormatterBuilder() .parseCaseInsensitive() .appendInstant() .toFormatter(ResolverStyle.STRICT); } //----------------------------------------------------------------------- /** * Returns the ISO date formatter that prints/parses a date without an offset, * such as '20111203'. *

* This returns an immutable formatter capable of printing and parsing * the ISO-8601 basic local date format. * The format consists of: *

    *
  • Four digits for the {@link ChronoField#YEAR year}. * Only years in the range 0000 to 9999 are supported. *
  • Two digits for the {@link ChronoField#MONTH_OF_YEAR month-of-year}. * This is pre-padded by zero to ensure two digits. *
  • Two digits for the {@link ChronoField#DAY_OF_MONTH day-of-month}. * This is pre-padded by zero to ensure two digits. *
  • If the offset is not available to print/parse then the format is complete. *
  • The {@link ZoneOffset#getId() offset ID} without colons. If the offset has * seconds then they will be handled even though this is not part of the ISO-8601 standard. * Parsing is case insensitive. *

* As this formatter has an optional element, it may be necessary to parse using * {@link DateTimeFormatter#parseBest}. */ public static final DateTimeFormatter BASIC_ISO_DATE; static { BASIC_ISO_DATE = new DateTimeFormatterBuilder() .parseCaseInsensitive() .appendValue(YEAR, 4) .appendValue(MONTH_OF_YEAR, 2) .appendValue(DAY_OF_MONTH, 2) .optionalStart() .appendOffset("+HHMMss", "Z") .toFormatter(ResolverStyle.STRICT).withChronology(IsoChronology.INSTANCE); } //----------------------------------------------------------------------- /** * Returns the RFC-1123 date-time formatter, such as 'Tue, 3 Jun 2008 11:05:30 GMT'. *

* This returns an immutable formatter capable of printing and parsing * most of the RFC-1123 format. * RFC-1123 updates RFC-822 changing the year from two digits to four. * This implementation requires a four digit year. * This implementation also does not handle North American or military zone * names, only 'GMT' and offset amounts. *

* The format consists of: *

    *
  • If the day-of-week is not available to print/parse then jump to day-of-month. *
  • Three letter {@link ChronoField#DAY_OF_WEEK day-of-week} in English. *
  • A comma *
  • A space *
  • One or two digits for the {@link ChronoField#DAY_OF_MONTH day-of-month}. *
  • A space *
  • Three letter {@link ChronoField#MONTH_OF_YEAR month-of-year} in English. *
  • A space *
  • Four digits for the {@link ChronoField#YEAR year}. * Only years in the range 0000 to 9999 are supported. *
  • A space *
  • Two digits for the {@link ChronoField#HOUR_OF_DAY hour-of-day}. * This is pre-padded by zero to ensure two digits. *
  • A colon *
  • Two digits for the {@link ChronoField#MINUTE_OF_HOUR minute-of-hour}. * This is pre-padded by zero to ensure two digits. *
  • If the second-of-minute is not available to print/parse then jump to the next space. *
  • A colon *
  • Two digits for the {@link ChronoField#SECOND_OF_MINUTE second-of-minute}. * This is pre-padded by zero to ensure two digits. *
  • A space *
  • The {@link ZoneOffset#getId() offset ID} without colons or seconds. * An offset of zero uses "GMT". North American zone names and military zone names are not handled. *

* Parsing is case insensitive. */ public static final DateTimeFormatter RFC_1123_DATE_TIME; static { // manually code maps to ensure correct data always used // (locale data can be changed by application code) Map dow = new HashMap(); dow.put(1L, "Mon"); dow.put(2L, "Tue"); dow.put(3L, "Wed"); dow.put(4L, "Thu"); dow.put(5L, "Fri"); dow.put(6L, "Sat"); dow.put(7L, "Sun"); Map moy = new HashMap(); moy.put(1L, "Jan"); moy.put(2L, "Feb"); moy.put(3L, "Mar"); moy.put(4L, "Apr"); moy.put(5L, "May"); moy.put(6L, "Jun"); moy.put(7L, "Jul"); moy.put(8L, "Aug"); moy.put(9L, "Sep"); moy.put(10L, "Oct"); moy.put(11L, "Nov"); moy.put(12L, "Dec"); RFC_1123_DATE_TIME = new DateTimeFormatterBuilder() .parseCaseInsensitive() .parseLenient() .optionalStart() .appendText(DAY_OF_WEEK, dow) .appendLiteral(", ") .optionalEnd() .appendValue(DAY_OF_MONTH, 1, 2, SignStyle.NOT_NEGATIVE) .appendLiteral(' ') .appendText(MONTH_OF_YEAR, moy) .appendLiteral(' ') .appendValue(YEAR, 4) // 2 digit year not handled .appendLiteral(' ') .appendValue(HOUR_OF_DAY, 2) .appendLiteral(':') .appendValue(MINUTE_OF_HOUR, 2) .optionalStart() .appendLiteral(':') .appendValue(SECOND_OF_MINUTE, 2) .optionalEnd() .appendLiteral(' ') .appendOffset("+HHMM", "GMT") // should handle UT/Z/EST/EDT/CST/CDT/MST/MDT/PST/MDT .toFormatter(ResolverStyle.SMART).withChronology(IsoChronology.INSTANCE); } //----------------------------------------------------------------------- /** * Creates a formatter using the specified pattern. *

* This method will create a formatter based on a simple pattern of letters and symbols. * For example, {@code d MMM yyyy} will format 2011-12-03 as '3 Dec 2011'. *

* The returned formatter will use the default locale, but this can be changed * using {@link DateTimeFormatter#withLocale(Locale)}. *

* All letters 'A' to 'Z' and 'a' to 'z' are reserved as pattern letters. * The following pattern letters are defined: *

     *  Symbol  Meaning                     Presentation      Examples
     *  ------  -------                     ------------      -------
     *   G       era                         number/text       1; 01; AD; Anno Domini
     *   y       year                        year              2004; 04
     *   D       day-of-year                 number            189
     *   M       month-of-year               number/text       7; 07; Jul; July; J
     *   d       day-of-month                number            10
     *
     *   Q       quarter-of-year             number/text       3; 03; Q3
     *   Y       week-based-year             year              1996; 96
     *   w       week-of-year                number            27
     *   W       week-of-month               number            27
     *   e       localized day-of-week       number            2; Tue; Tuesday; T
     *   E       day-of-week                 number/text       2; Tue; Tuesday; T
     *   F       week-of-month               number            3
     *
     *   a       am-pm-of-day                text              PM
     *   h       clock-hour-of-am-pm (1-12)  number            12
     *   K       hour-of-am-pm (0-11)        number            0
     *   k       clock-hour-of-am-pm (1-24)  number            0
     *
     *   H       hour-of-day (0-23)          number            0
     *   m       minute-of-hour              number            30
     *   s       second-of-minute            number            55
     *   S       fraction-of-second          fraction          978
     *   A       milli-of-day                number            1234
     *   n       nano-of-second              number            987654321
     *   N       nano-of-day                 number            1234000000
     *
     *   V       time-zone ID                zone-id           America/Los_Angeles; Z; -08:30
     *   z       time-zone name              zone-name         Pacific Standard Time; PST
     *   X       zone-offset 'Z' for zero    offset-X          Z; -08; -0830; -08:30; -083015; -08:30:15;
     *   x       zone-offset                 offset-x          +0000; -08; -0830; -08:30; -083015; -08:30:15;
     *   Z       zone-offset                 offset-Z          +0000; -0800; -08:00;
     *
     *   p       pad next                    pad modifier      1
     *
     *   '       escape for text             delimiter
     *   ''      single quote                literal           '
     *   [       optional section start
     *   ]       optional section end
     *   {}      reserved for future use
     * 
*

* The count of pattern letters determine the format. *

* Text: The text style is determined based on the number of pattern letters used. * Less than 4 pattern letters will use the {@link TextStyle#SHORT short form}. * Exactly 4 pattern letters will use the {@link TextStyle#FULL full form}. * Exactly 5 pattern letters will use the {@link TextStyle#NARROW narrow form}. *

* Number: If the count of letters is one, then the value is printed using the minimum number * of digits and without padding as per {@link DateTimeFormatterBuilder#appendValue(TemporalField)}. * Otherwise, the count of digits is used as the width of the output field as per * {@link DateTimeFormatterBuilder#appendValue(TemporalField, int)}. *

* Number/Text: If the count of pattern letters is 3 or greater, use the Text rules above. * Otherwise use the Number rules above. *

* Fraction: Outputs the nano-of-second field as a fraction-of-second. * The nano-of-second value has nine digits, thus the count of pattern letters is from 1 to 9. * If it is less than 9, then the nano-of-second value is truncated, with only the most * significant digits being output. * When parsing in strict mode, the number of parsed digits must match the count of pattern letters. * When parsing in lenient mode, the number of parsed digits must be at least the count of pattern * letters, up to 9 digits. *

* Year: The count of letters determines the minimum field width below which padding is used. * If the count of letters is two, then a {@link DateTimeFormatterBuilder#appendValueReduced reduced} * two digit form is used. * For printing, this outputs the rightmost two digits. For parsing, this will parse using the * base value of 2000, resulting in a year within the range 2000 to 2099 inclusive. * If the count of letters is less than four (but not two), then the sign is only output for negative * years as per {@link SignStyle#NORMAL}. * Otherwise, the sign is output if the pad width is exceeded, as per {@link SignStyle#EXCEEDS_PAD} *

* ZoneId: This outputs the time-zone ID, such as 'Europe/Paris'. * If the count of letters is two, then the time-zone ID is output. * Any other count of letters throws {@code IllegalArgumentException}. *

* Zone names: This outputs the display name of the time-zone ID. * If the count of letters is one, two or three, then the short name is output. * If the count of letters is four, then the full name is output. * Five or more letters throws {@code IllegalArgumentException}. *

* Offset X and x: This formats the offset based on the number of pattern letters. * One letter outputs just the hour', such as '+01', unless the minute is non-zero * in which case the minute is also output, such as '+0130'. * Two letters outputs the hour and minute, without a colon, such as '+0130'. * Three letters outputs the hour and minute, with a colon, such as '+01:30'. * Four letters outputs the hour and minute and optional second, without a colon, such as '+013015'. * Five letters outputs the hour and minute and optional second, with a colon, such as '+01:30:15'. * Six or more letters throws {@code IllegalArgumentException}. * Pattern letter 'X' (upper case) will output 'Z' when the offset to be output would be zero, * whereas pattern letter 'x' (lower case) will output '+00', '+0000', or '+00:00'. *

* Offset Z: This formats the offset based on the number of pattern letters. * One, two or three letters outputs the hour and minute, without a colon, such as '+0130'. * Four or more letters throws {@code IllegalArgumentException}. * The output will be '+0000' when the offset is zero. *

* Optional section: The optional section markers work exactly like calling * {@link DateTimeFormatterBuilder#optionalStart()} and {@link DateTimeFormatterBuilder#optionalEnd()}. *

* Pad modifier: Modifies the pattern that immediately follows to be padded with spaces. * The pad width is determined by the number of pattern letters. * This is the same as calling {@link DateTimeFormatterBuilder#padNext(int)}. *

* For example, 'ppH' outputs the hour-of-day padded on the left with spaces to a width of 2. *

* Any unrecognized letter is an error. * Any non-letter character, other than '[', ']', '{', '}' and the single quote will be output directly. * Despite this, it is recommended to use single quotes around all characters that you want to * output directly to ensure that future changes do not break your application. * * @param pattern the pattern to use, not null * @return the formatter based on the pattern, not null * @throws IllegalArgumentException if the pattern is invalid * @see DateTimeFormatterBuilder#appendPattern(String) */ public static DateTimeFormatter ofPattern(String pattern) { return new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter(); } /** * Creates a formatter using the specified pattern. *

* This method will create a formatter based on a simple pattern of letters and symbols. * For example, {@code d MMM yyyy} will format 2011-12-03 as '3 Dec 2011'. *

* See {@link #ofPattern(String)} for details of the pattern. *

* The returned formatter will use the specified locale, but this can be changed * using {@link DateTimeFormatter#withLocale(Locale)}. * * @param pattern the pattern to use, not null * @param locale the locale to use, not null * @return the formatter based on the pattern, not null * @throws IllegalArgumentException if the pattern is invalid * @see DateTimeFormatterBuilder#appendPattern(String) */ public static DateTimeFormatter ofPattern(String pattern, Locale locale) { return new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter(locale); } //----------------------------------------------------------------------- /** * Returns a locale specific date format. *

* This returns a formatter that will print/parse a date. * The exact format pattern used varies by locale. *

* The locale is determined from the formatter. The formatter returned directly by * this method will use the {@link Locale#getDefault() default locale}. * The locale can be controlled using {@link DateTimeFormatter#withLocale(Locale) withLocale(Locale)} * on the result of this method. *

* Note that the localized pattern is looked up lazily. * This {@code DateTimeFormatter} holds the style required and the locale, * looking up the pattern required on demand. * * @param dateStyle the formatter style to obtain, not null * @return the date formatter, not null */ public static DateTimeFormatter ofLocalizedDate(FormatStyle dateStyle) { Jdk8Methods.requireNonNull(dateStyle, "dateStyle"); return new DateTimeFormatterBuilder().appendLocalized(dateStyle, null) .toFormatter().withChronology(IsoChronology.INSTANCE); } /** * Returns a locale specific time format. *

* This returns a formatter that will print/parse a time. * The exact format pattern used varies by locale. *

* The locale is determined from the formatter. The formatter returned directly by * this method will use the {@link Locale#getDefault() default locale}. * The locale can be controlled using {@link DateTimeFormatter#withLocale(Locale) withLocale(Locale)} * on the result of this method. *

* Note that the localized pattern is looked up lazily. * This {@code DateTimeFormatter} holds the style required and the locale, * looking up the pattern required on demand. * * @param timeStyle the formatter style to obtain, not null * @return the time formatter, not null */ public static DateTimeFormatter ofLocalizedTime(FormatStyle timeStyle) { Jdk8Methods.requireNonNull(timeStyle, "timeStyle"); return new DateTimeFormatterBuilder().appendLocalized(null, timeStyle) .toFormatter().withChronology(IsoChronology.INSTANCE); } /** * Returns a locale specific date-time format, which is typically of short length. *

* This returns a formatter that will print/parse a date-time. * The exact format pattern used varies by locale. *

* The locale is determined from the formatter. The formatter returned directly by * this method will use the {@link Locale#getDefault() default locale}. * The locale can be controlled using {@link DateTimeFormatter#withLocale(Locale) withLocale(Locale)} * on the result of this method. *

* Note that the localized pattern is looked up lazily. * This {@code DateTimeFormatter} holds the style required and the locale, * looking up the pattern required on demand. * * @param dateTimeStyle the formatter style to obtain, not null * @return the date-time formatter, not null */ public static DateTimeFormatter ofLocalizedDateTime(FormatStyle dateTimeStyle) { Jdk8Methods.requireNonNull(dateTimeStyle, "dateTimeStyle"); return new DateTimeFormatterBuilder().appendLocalized(dateTimeStyle, dateTimeStyle) .toFormatter().withChronology(IsoChronology.INSTANCE); } /** * Returns a locale specific date and time format. *

* This returns a formatter that will print/parse a date-time. * The exact format pattern used varies by locale. *

* The locale is determined from the formatter. The formatter returned directly by * this method will use the {@link Locale#getDefault() default locale}. * The locale can be controlled using {@link DateTimeFormatter#withLocale(Locale) withLocale(Locale)} * on the result of this method. *

* Note that the localized pattern is looked up lazily. * This {@code DateTimeFormatter} holds the style required and the locale, * looking up the pattern required on demand. * * @param dateStyle the date formatter style to obtain, not null * @param timeStyle the time formatter style to obtain, not null * @return the date, time or date-time formatter, not null */ public static DateTimeFormatter ofLocalizedDateTime(FormatStyle dateStyle, FormatStyle timeStyle) { Jdk8Methods.requireNonNull(dateStyle, "dateStyle"); Jdk8Methods.requireNonNull(timeStyle, "timeStyle"); return new DateTimeFormatterBuilder().appendLocalized(dateStyle, timeStyle) .toFormatter().withChronology(IsoChronology.INSTANCE); } //----------------------------------------------------------------------- /** * A query that provides access to the excess days that were parsed. *

* This returns a singleton {@linkplain TemporalQuery query} that provides * access to additional information from the parse. The query always returns * a non-null period, with a zero period returned instead of null. *

* There are two situations where this query may return a non-zero period. *

    *
  • If the {@code ResolverStyle} is {@code LENIENT} and a time is parsed * without a date, then the complete result of the parse consists of a * {@code LocalTime} and an excess {@code Period} in days. * *
  • If the {@code ResolverStyle} is {@code SMART} and a time is parsed * without a date where the time is 24:00:00, then the complete result of * the parse consists of a {@code LocalTime} of 00:00:00 and an excess * {@code Period} of one day. *
*

* In both cases, if a complete {@code ChronoLocalDateTime} or {@code Instant} * is parsed, then the excess days are added to the date part. * As a result, this query will return a zero period. *

* The {@code SMART} behaviour handles the common "end of day" 24:00 value. * Processing in {@code LENIENT} mode also produces the same result: *

     *  Text to parse        Parsed object                         Excess days
     *  "2012-12-03T00:00"   LocalDateTime.of(2012, 12, 3, 0, 0)   ZERO
     *  "2012-12-03T24:00"   LocalDateTime.of(2012, 12, 4, 0, 0)   ZERO
     *  "00:00"              LocalTime.of(0, 0)                    ZERO
     *  "24:00"              LocalTime.of(0, 0)                    Period.ofDays(1)
     * 
* The query can be used as follows: *
     *  TemporalAccessor parsed = formatter.parse(str);
     *  LocalTime time = parsed.query(LocalTime.FROM);
     *  Period extraDays = parsed.query(DateTimeFormatter.parsedExcessDays());
     * 
* @return a query that provides access to the excess days that were parsed */ public static final TemporalQuery parsedExcessDays() { return PARSED_EXCESS_DAYS; } private static final TemporalQuery PARSED_EXCESS_DAYS = new TemporalQuery() { public Period queryFrom(TemporalAccessor temporal) { if (temporal instanceof DateTimeBuilder) { return ((DateTimeBuilder) temporal).excessDays; } else { return Period.ZERO; } } }; /** * A query that provides access to whether a leap-second was parsed. *

* This returns a singleton {@linkplain TemporalQuery query} that provides * access to additional information from the parse. The query always returns * a non-null boolean, true if parsing saw a leap-second, false if not. *

* Instant parsing handles the special "leap second" time of '23:59:60'. * Leap seconds occur at '23:59:60' in the UTC time-zone, but at other * local times in different time-zones. To avoid this potential ambiguity, * the handling of leap-seconds is limited to * {@link DateTimeFormatterBuilder#appendInstant()}, as that method * always parses the instant with the UTC zone offset. *

* If the time '23:59:60' is received, then a simple conversion is applied, * replacing the second-of-minute of 60 with 59. This query can be used * on the parse result to determine if the leap-second adjustment was made. * The query will return one second of excess if it did adjust to remove * the leap-second, and zero if not. Note that applying a leap-second * smoothing mechanism, such as UTC-SLS, is the responsibility of the * application, as follows: *

     *  TemporalAccessor parsed = formatter.parse(str);
     *  Instant instant = parsed.query(Instant::from);
     *  if (parsed.query(DateTimeFormatter.parsedLeapSecond())) {
     *    // validate leap-second is correct and apply correct smoothing
     *  }
     * 
* @return a query that provides access to whether a leap-second was parsed */ public static final TemporalQuery parsedLeapSecond() { return PARSED_LEAP_SECOND; } private static final TemporalQuery PARSED_LEAP_SECOND = new TemporalQuery() { public Boolean queryFrom(TemporalAccessor temporal) { if (temporal instanceof DateTimeBuilder) { return ((DateTimeBuilder) temporal).leapSecond; } else { return Boolean.FALSE; } } }; //----------------------------------------------------------------------- /** * The printer and/or parser to use, not null. */ private final CompositePrinterParser printerParser; /** * The locale to use for formatting, not null. */ private final Locale locale; /** * The symbols to use for formatting, not null. */ private final DecimalStyle decimalStyle; /** * The resolver style to use, not null. */ private final ResolverStyle resolverStyle; /** * The fields to use in resolving, null for all fields. */ private final Set resolverFields; /** * The chronology to use for formatting, null for no override. */ private final Chronology chrono; /** * The zone to use for formatting, null for no override. */ private final ZoneId zone; //----------------------------------------------------------------------- /** * Constructor. * * @param printerParser the printer/parser to use, not null * @param locale the locale to use, not null * @param decimalStyle the decimal style to use, not null * @param resolverStyle the resolver style to use, not null * @param resolverFields the fields to use during resolving, null for all fields * @param chrono the chronology to use, null for no override * @param zone the zone to use, null for no override */ DateTimeFormatter(CompositePrinterParser printerParser, Locale locale, DecimalStyle decimalStyle, ResolverStyle resolverStyle, Set resolverFields, Chronology chrono, ZoneId zone) { this.printerParser = Jdk8Methods.requireNonNull(printerParser, "printerParser"); this.locale = Jdk8Methods.requireNonNull(locale, "locale"); this.decimalStyle = Jdk8Methods.requireNonNull(decimalStyle, "decimalStyle"); this.resolverStyle = Jdk8Methods.requireNonNull(resolverStyle, "resolverStyle"); this.resolverFields = resolverFields; this.chrono = chrono; this.zone = zone; } //----------------------------------------------------------------------- /** * Gets the locale to be used during formatting. *

* This is used to lookup any part of the formatter needing specific * localization, such as the text or localized pattern. * * @return the locale of this formatter, not null */ public Locale getLocale() { return locale; } /** * Returns a copy of this formatter with a new locale. *

* This is used to lookup any part of the formatter needing specific * localization, such as the text or localized pattern. *

* This instance is immutable and unaffected by this method call. * * @param locale the new locale, not null * @return a formatter based on this formatter with the requested locale, not null */ public DateTimeFormatter withLocale(Locale locale) { if (this.locale.equals(locale)) { return this; } return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); } //----------------------------------------------------------------------- /** * Gets the decimal style to be used during formatting. * * @return the decimal style of this formatter, not null */ public DecimalStyle getDecimalStyle() { return decimalStyle; } /** * Returns a copy of this formatter with a new decimal style. *

* This instance is immutable and unaffected by this method call. * * @param decimalStyle the new decimal style, not null * @return a formatter based on this formatter with the requested symbols, not null */ public DateTimeFormatter withDecimalStyle(DecimalStyle decimalStyle) { if (this.decimalStyle.equals(decimalStyle)) { return this; } return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); } //----------------------------------------------------------------------- /** * Gets the overriding chronology to be used during formatting. *

* This returns the override chronology, used to convert dates. * By default, a formatter has no override chronology, returning null. * See {@link #withChronology(Chronology)} for more details on overriding. * * @return the chronology of this formatter, null if no override */ public Chronology getChronology() { return chrono; } /** * Returns a copy of this formatter with a new override chronology. *

* This returns a formatter with similar state to this formatter but * with the override chronology set. * By default, a formatter has no override chronology, returning null. *

* If an override is added, then any date that is printed or parsed will be affected. *

* When printing, if the {@code Temporal} object contains a date then it will * be converted to a date in the override chronology. * Any time or zone will be retained unless overridden. * The converted result will behave in a manner equivalent to an implementation * of {@code ChronoLocalDate},{@code ChronoLocalDateTime} or {@code ChronoZonedDateTime}. *

* When parsing, the override chronology will be used to interpret the * {@linkplain ChronoField fields} into a date unless the * formatter directly parses a valid chronology. *

* This instance is immutable and unaffected by this method call. * * @param chrono the new chronology, not null * @return a formatter based on this formatter with the requested override chronology, not null */ public DateTimeFormatter withChronology(Chronology chrono) { if (Jdk8Methods.equals(this.chrono, chrono)) { return this; } return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); } //----------------------------------------------------------------------- /** * Gets the overriding zone to be used during formatting. *

* This returns the override zone, used to convert instants. * By default, a formatter has no override zone, returning null. * See {@link #withZone(ZoneId)} for more details on overriding. * * @return the chronology of this formatter, null if no override */ public ZoneId getZone() { return zone; } /** * Returns a copy of this formatter with a new override zone. *

* This returns a formatter with similar state to this formatter but * with the override zone set. * By default, a formatter has no override zone, returning null. *

* If an override is added, then any instant that is printed or parsed will be affected. *

* When printing, if the {@code Temporal} object contains an instant then it will * be converted to a zoned date-time using the override zone. * If the input has a chronology then it will be retained unless overridden. * If the input does not have a chronology, such as {@code Instant}, then * the ISO chronology will be used. * The converted result will behave in a manner equivalent to an implementation * of {@code ChronoZonedDateTime}. *

* When parsing, the override zone will be used to interpret the * {@linkplain ChronoField fields} into an instant unless the * formatter directly parses a valid zone. *

* This instance is immutable and unaffected by this method call. * * @param zone the new override zone, not null * @return a formatter based on this formatter with the requested override zone, not null */ public DateTimeFormatter withZone(ZoneId zone) { if (Jdk8Methods.equals(this.zone, zone)) { return this; } return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); } //----------------------------------------------------------------------- /** * Gets the resolver style to use during parsing. *

* This returns the resolver style, used during the second phase of parsing * when fields are resolved into dates and times. * By default, a formatter has the {@link ResolverStyle#SMART SMART} resolver style. * See {@link #withResolverStyle(ResolverStyle)} for more details. * * @return the resolver style of this formatter, not null */ public ResolverStyle getResolverStyle() { return resolverStyle; } /** * Returns a copy of this formatter with a new resolver style. *

* This returns a formatter with similar state to this formatter but * with the resolver style set. By default, a formatter has the * {@link ResolverStyle#SMART SMART} resolver style. *

* Changing the resolver style only has an effect during parsing. * Parsing a text string occurs in two phases. * Phase 1 is a basic text parse according to the fields added to the builder. * Phase 2 resolves the parsed field-value pairs into date and/or time objects. * The resolver style is used to control how phase 2, resolving, happens. * See {@code ResolverStyle} for more information on the options available. *

* This instance is immutable and unaffected by this method call. * * @param resolverStyle the new resolver style, not null * @return a formatter based on this formatter with the requested resolver style, not null */ public DateTimeFormatter withResolverStyle(ResolverStyle resolverStyle) { Jdk8Methods.requireNonNull(resolverStyle, "resolverStyle"); if (Jdk8Methods.equals(this.resolverStyle, resolverStyle)) { return this; } return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); } //----------------------------------------------------------------------- /** * Gets the resolver fields to use during parsing. *

* This returns the resolver fields, used during the second phase of parsing * when fields are resolved into dates and times. * By default, a formatter has no resolver fields, and thus returns null. * See {@link #withResolverFields(Set)} for more details. * * @return the immutable set of resolver fields of this formatter, null if no fields */ public Set getResolverFields() { return resolverFields; } /** * Returns a copy of this formatter with a new set of resolver fields. *

* This returns a formatter with similar state to this formatter but with * the resolver fields set. By default, a formatter has no resolver fields. *

* Changing the resolver fields only has an effect during parsing. * Parsing a text string occurs in two phases. * Phase 1 is a basic text parse according to the fields added to the builder. * Phase 2 resolves the parsed field-value pairs into date and/or time objects. * The resolver fields are used to filter the field-value pairs between phase 1 and 2. *

* This can be used to select between two or more ways that a date or time might * be resolved. For example, if the formatter consists of year, month, day-of-month * and day-of-year, then there are two ways to resolve a date. * Calling this method with the arguments {@link ChronoField#YEAR YEAR} and * {@link ChronoField#DAY_OF_YEAR DAY_OF_YEAR} will ensure that the date is * resolved using the year and day-of-year, effectively meaning that the month * and day-of-month are ignored during the resolving phase. *

* In a similar manner, this method can be used to ignore secondary fields that * would otherwise be cross-checked. For example, if the formatter consists of year, * month, day-of-month and day-of-week, then there is only one way to resolve a * date, but the parsed value for day-of-week will be cross-checked against the * resolved date. Calling this method with the arguments {@link ChronoField#YEAR YEAR}, * {@link ChronoField#MONTH_OF_YEAR MONTH_OF_YEAR} and * {@link ChronoField#DAY_OF_MONTH DAY_OF_MONTH} will ensure that the date is * resolved correctly, but without any cross-check for the day-of-week. *

* In implementation terms, this method behaves as follows. The result of the * parsing phase can be considered to be a map of field to value. The behavior * of this method is to cause that map to be filtered between phase 1 and 2, * removing all fields other than those specified as arguments to this method. *

* This instance is immutable and unaffected by this method call. * * @param resolverFields the new set of resolver fields, null if no fields * @return a formatter based on this formatter with the requested resolver style, not null */ public DateTimeFormatter withResolverFields(TemporalField... resolverFields) { if (resolverFields == null) { return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, null, chrono, zone); } Set fields = new HashSet(Arrays.asList(resolverFields)); if (Jdk8Methods.equals(this.resolverFields, fields)) { return this; } fields = Collections.unmodifiableSet(fields); return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, fields, chrono, zone); } /** * Returns a copy of this formatter with a new set of resolver fields. *

* This returns a formatter with similar state to this formatter but with * the resolver fields set. By default, a formatter has no resolver fields. *

* Changing the resolver fields only has an effect during parsing. * Parsing a text string occurs in two phases. * Phase 1 is a basic text parse according to the fields added to the builder. * Phase 2 resolves the parsed field-value pairs into date and/or time objects. * The resolver fields are used to filter the field-value pairs between phase 1 and 2. *

* This can be used to select between two or more ways that a date or time might * be resolved. For example, if the formatter consists of year, month, day-of-month * and day-of-year, then there are two ways to resolve a date. * Calling this method with the arguments {@link ChronoField#YEAR YEAR} and * {@link ChronoField#DAY_OF_YEAR DAY_OF_YEAR} will ensure that the date is * resolved using the year and day-of-year, effectively meaning that the month * and day-of-month are ignored during the resolving phase. *

* In a similar manner, this method can be used to ignore secondary fields that * would otherwise be cross-checked. For example, if the formatter consists of year, * month, day-of-month and day-of-week, then there is only one way to resolve a * date, but the parsed value for day-of-week will be cross-checked against the * resolved date. Calling this method with the arguments {@link ChronoField#YEAR YEAR}, * {@link ChronoField#MONTH_OF_YEAR MONTH_OF_YEAR} and * {@link ChronoField#DAY_OF_MONTH DAY_OF_MONTH} will ensure that the date is * resolved correctly, but without any cross-check for the day-of-week. *

* In implementation terms, this method behaves as follows. The result of the * parsing phase can be considered to be a map of field to value. The behavior * of this method is to cause that map to be filtered between phase 1 and 2, * removing all fields other than those specified as arguments to this method. *

* This instance is immutable and unaffected by this method call. * * @param resolverFields the new set of resolver fields, null if no fields * @return a formatter based on this formatter with the requested resolver style, not null */ public DateTimeFormatter withResolverFields(Set resolverFields) { if (resolverFields == null) { return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, null, chrono, zone); } if (Jdk8Methods.equals(this.resolverFields, resolverFields)) { return this; } resolverFields = Collections.unmodifiableSet(new HashSet(resolverFields)); return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); } //----------------------------------------------------------------------- /** * Formats a date-time object using this formatter. *

* This formats the date-time to a String using the rules of the formatter. * * @param temporal the temporal object to print, not null * @return the printed string, not null * @throws DateTimeException if an error occurs during formatting */ public String format(TemporalAccessor temporal) { StringBuilder buf = new StringBuilder(32); formatTo(temporal, buf); return buf.toString(); } //----------------------------------------------------------------------- /** * Formats a date-time object to an {@code Appendable} using this formatter. *

* This formats the date-time to the specified destination. * {@link Appendable} is a general purpose interface that is implemented by all * key character output classes including {@code StringBuffer}, {@code StringBuilder}, * {@code PrintStream} and {@code Writer}. *

* Although {@code Appendable} methods throw an {@code IOException}, this method does not. * Instead, any {@code IOException} is wrapped in a runtime exception. * * @param temporal the temporal object to print, not null * @param appendable the appendable to print to, not null * @throws DateTimeException if an error occurs during formatting */ public void formatTo(TemporalAccessor temporal, Appendable appendable) { Jdk8Methods.requireNonNull(temporal, "temporal"); Jdk8Methods.requireNonNull(appendable, "appendable"); try { DateTimePrintContext context = new DateTimePrintContext(temporal, this); if (appendable instanceof StringBuilder) { printerParser.print(context, (StringBuilder) appendable); } else { // buffer output to avoid writing to appendable in case of error StringBuilder buf = new StringBuilder(32); printerParser.print(context, buf); appendable.append(buf); } } catch (IOException ex) { throw new DateTimeException(ex.getMessage(), ex); } } //----------------------------------------------------------------------- /** * Fully parses the text producing a temporal object. *

* This parses the entire text producing a temporal object. * It is typically more useful to use {@link #parse(CharSequence, TemporalQuery)}. * The result of this method is {@code TemporalAccessor} which has been resolved, * applying basic validation checks to help ensure a valid date-time. *

* If the parse completes without reading the entire length of the text, * or a problem occurs during parsing or merging, then an exception is thrown. * * @param text the text to parse, not null * @return the parsed temporal object, not null * @throws DateTimeParseException if unable to parse the requested result */ public TemporalAccessor parse(CharSequence text) { Jdk8Methods.requireNonNull(text, "text"); try { return parseToBuilder(text, null).resolve(resolverStyle, resolverFields); } catch (DateTimeParseException ex) { throw ex; } catch (RuntimeException ex) { throw createError(text, ex); } } /** * Parses the text using this formatter, providing control over the text position. *

* This parses the text without requiring the parse to start from the beginning * of the string or finish at the end. * The result of this method is {@code TemporalAccessor} which has been resolved, * applying basic validation checks to help ensure a valid date-time. *

* The text will be parsed from the specified start {@code ParsePosition}. * The entire length of the text does not have to be parsed, the {@code ParsePosition} * will be updated with the index at the end of parsing. *

* The operation of this method is slightly different to similar methods using * {@code ParsePosition} on {@code java.text.Format}. That class will return * errors using the error index on the {@code ParsePosition}. By contrast, this * method will throw a {@link DateTimeParseException} if an error occurs, with * the exception containing the error index. * This change in behavior is necessary due to the increased complexity of * parsing and resolving dates/times in this API. *

* If the formatter parses the same field more than once with different values, * the result will be an error. * * @param text the text to parse, not null * @param position the position to parse from, updated with length parsed * and the index of any error, not null * @return the parsed temporal object, not null * @throws DateTimeParseException if unable to parse the requested result * @throws IndexOutOfBoundsException if the position is invalid */ public TemporalAccessor parse(CharSequence text, ParsePosition position) { Jdk8Methods.requireNonNull(text, "text"); Jdk8Methods.requireNonNull(position, "position"); try { return parseToBuilder(text, position).resolve(resolverStyle, resolverFields); } catch (DateTimeParseException ex) { throw ex; } catch (IndexOutOfBoundsException ex) { throw ex; } catch (RuntimeException ex) { throw createError(text, ex); } } //----------------------------------------------------------------------- /** * Fully parses the text producing an object of the specified type. *

* Most applications should use this method for parsing. * It parses the entire text to produce the required date-time. * For example: *

     *  LocalDateTime dt = parser.parse(str, LocalDateTime.FROM);
     * 
* If the parse completes without reading the entire length of the text, * or a problem occurs during parsing or merging, then an exception is thrown. * * @param the type to extract * @param text the text to parse, not null * @param type the type to extract, not null * @return the parsed date-time, not null * @throws DateTimeParseException if unable to parse the requested result */ public T parse(CharSequence text, TemporalQuery type) { Jdk8Methods.requireNonNull(text, "text"); Jdk8Methods.requireNonNull(type, "type"); try { DateTimeBuilder builder = parseToBuilder(text, null).resolve(resolverStyle, resolverFields); return builder.build(type); } catch (DateTimeParseException ex) { throw ex; } catch (RuntimeException ex) { throw createError(text, ex); } } /** * Fully parses the text producing an object of one of the specified types. *

* This parse method is convenient for use when the parser can handle optional elements. * For example, a pattern of 'yyyy[-MM[-dd]]' can be fully parsed to a {@code LocalDate}, * or partially parsed to a {@code YearMonth} or a {@code Year}. * The types must be specified in order, starting from the best matching full-parse option * and ending with the worst matching minimal parse option. *

* The result is associated with the first type that successfully parses. * Normally, applications will use {@code instanceof} to check the result. * For example: *

     *  TemporalAccessor dt = parser.parseBest(str, LocalDate.FROM, YearMonth.FROM);
     *  if (dt instanceof LocalDate) {
     *   ...
     *  } else {
     *   ...
     *  }
     * 
* If the parse completes without reading the entire length of the text, * or a problem occurs during parsing or merging, then an exception is thrown. * * @param text the text to parse, not null * @param types the types to attempt to parse to, which must implement {@code TemporalAccessor}, not null * @return the parsed date-time, not null * @throws IllegalArgumentException if less than 2 types are specified * @throws DateTimeParseException if unable to parse the requested result */ public TemporalAccessor parseBest(CharSequence text, TemporalQuery... types) { Jdk8Methods.requireNonNull(text, "text"); Jdk8Methods.requireNonNull(types, "types"); if (types.length < 2) { throw new IllegalArgumentException("At least two types must be specified"); } try { DateTimeBuilder builder = parseToBuilder(text, null).resolve(resolverStyle, resolverFields); for (TemporalQuery type : types) { try { return (TemporalAccessor) builder.build(type); } catch (RuntimeException ex) { // continue } } throw new DateTimeException("Unable to convert parsed text to any specified type: " + Arrays.toString(types)); } catch (DateTimeParseException ex) { throw ex; } catch (RuntimeException ex) { throw createError(text, ex); } } private DateTimeParseException createError(CharSequence text, RuntimeException ex) { String abbr = ""; if (text.length() > 64) { abbr = text.subSequence(0, 64).toString() + "..."; } else { abbr = text.toString(); } return new DateTimeParseException("Text '" + abbr + "' could not be parsed: " + ex.getMessage(), text, 0, ex); } //----------------------------------------------------------------------- /** * Parses the text to a builder. *

* This parses to a {@code DateTimeBuilder} ensuring that the text is fully parsed. * This method throws {@link DateTimeParseException} if unable to parse, or * some other {@code DateTimeException} if another date/time problem occurs. * * @param text the text to parse, not null * @param position the position to parse from, updated with length parsed * and the index of any error, null if parsing whole string * @return the engine representing the result of the parse, not null * @throws DateTimeParseException if the parse fails */ private DateTimeBuilder parseToBuilder(final CharSequence text, final ParsePosition position) { ParsePosition pos = (position != null ? position : new ParsePosition(0)); Parsed result = parseUnresolved0(text, pos); if (result == null || pos.getErrorIndex() >= 0 || (position == null && pos.getIndex() < text.length())) { String abbr = ""; if (text.length() > 64) { abbr = text.subSequence(0, 64).toString() + "..."; } else { abbr = text.toString(); } if (pos.getErrorIndex() >= 0) { throw new DateTimeParseException("Text '" + abbr + "' could not be parsed at index " + pos.getErrorIndex(), text, pos.getErrorIndex()); } else { throw new DateTimeParseException("Text '" + abbr + "' could not be parsed, unparsed text found at index " + pos.getIndex(), text, pos.getIndex()); } } return result.toBuilder(); } /** * Parses the text using this formatter, without resolving the result, intended * for advanced use cases. *

* Parsing is implemented as a two-phase operation. * First, the text is parsed using the layout defined by the formatter, producing * a {@code Map} of field to value, a {@code ZoneId} and a {@code Chronology}. * Second, the parsed data is resolved, by validating, combining and * simplifying the various fields into more useful ones. * This method performs the parsing stage but not the resolving stage. *

* The result of this method is {@code TemporalAccessor} which represents the * data as seen in the input. Values are not validated, thus parsing a date string * of '2012-00-65' would result in a temporal with three fields - year of '2012', * month of '0' and day-of-month of '65'. *

* The text will be parsed from the specified start {@code ParsePosition}. * The entire length of the text does not have to be parsed, the {@code ParsePosition} * will be updated with the index at the end of parsing. *

* Errors are returned using the error index field of the {@code ParsePosition} * instead of {@code DateTimeParseException}. * The returned error index will be set to an index indicative of the error. * Callers must check for errors before using the context. *

* If the formatter parses the same field more than once with different values, * the result will be an error. *

* This method is intended for advanced use cases that need access to the * internal state during parsing. Typical application code should use * {@link #parse(CharSequence, TemporalQuery)} or the parse method on the target type. * * @param text the text to parse, not null * @param position the position to parse from, updated with length parsed * and the index of any error, not null * @return the parsed text, null if the parse results in an error * @throws DateTimeException if some problem occurs during parsing * @throws IndexOutOfBoundsException if the position is invalid */ public TemporalAccessor parseUnresolved(CharSequence text, ParsePosition position) { return parseUnresolved0(text, position); } private Parsed parseUnresolved0(CharSequence text, ParsePosition position) { Jdk8Methods.requireNonNull(text, "text"); Jdk8Methods.requireNonNull(position, "position"); DateTimeParseContext context = new DateTimeParseContext(this); int pos = position.getIndex(); pos = printerParser.parse(context, text, pos); if (pos < 0) { position.setErrorIndex(~pos); // index not updated from input return null; } position.setIndex(pos); // errorIndex not updated from input return context.toParsed(); } //----------------------------------------------------------------------- /** * Returns the formatter as a composite printer parser. * * @param optional whether the printer/parser should be optional * @return the printer/parser, not null */ CompositePrinterParser toPrinterParser(boolean optional) { return printerParser.withOptional(optional); } /** * Returns this formatter as a {@code java.text.Format} instance. *

* The returned {@link Format} instance will print any {@link TemporalAccessor} * and parses to a resolved {@link TemporalAccessor}. *

* Exceptions will follow the definitions of {@code Format}, see those methods * for details about {@code IllegalArgumentException} during formatting and * {@code ParseException} or null during parsing. * The format does not support attributing of the returned format string. * * @return this formatter as a classic format instance, not null */ public Format toFormat() { return new ClassicFormat(this, null); } /** * Returns this formatter as a {@code java.text.Format} instance that will * parse to the specified type. *

* The returned {@link Format} instance will print any {@link TemporalAccessor} * and parses to the type specified. * The type must be one that is supported by {@link #parse}. *

* Exceptions will follow the definitions of {@code Format}, see those methods * for details about {@code IllegalArgumentException} during formatting and * {@code ParseException} or null during parsing. * The format does not support attributing of the returned format string. * * @param query the query to parse to, not null * @return this formatter as a classic format instance, not null */ public Format toFormat(TemporalQuery query) { Jdk8Methods.requireNonNull(query, "query"); return new ClassicFormat(this, query); } //----------------------------------------------------------------------- /** * Returns a description of the underlying formatters. * * @return a description of this formatter, not null */ @Override public String toString() { String pattern = printerParser.toString(); return pattern.startsWith("[") ? pattern : pattern.substring(1, pattern.length() - 1); } //----------------------------------------------------------------------- /** * Implements the classic Java Format API. * @serial exclude */ @SuppressWarnings("serial") // not actually serializable static class ClassicFormat extends Format { /** The formatter. */ private final DateTimeFormatter formatter; /** The query to be parsed. */ private final TemporalQuery query; /** Constructor. */ public ClassicFormat(DateTimeFormatter formatter, TemporalQuery query) { this.formatter = formatter; this.query = query; } @Override public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { Jdk8Methods.requireNonNull(obj, "obj"); Jdk8Methods.requireNonNull(toAppendTo, "toAppendTo"); Jdk8Methods.requireNonNull(pos, "pos"); if (obj instanceof TemporalAccessor == false) { throw new IllegalArgumentException("Format target must implement TemporalAccessor"); } pos.setBeginIndex(0); pos.setEndIndex(0); try { formatter.formatTo((TemporalAccessor) obj, toAppendTo); } catch (RuntimeException ex) { throw new IllegalArgumentException(ex.getMessage(), ex); } return toAppendTo; } @Override public Object parseObject(String text) throws ParseException { Jdk8Methods.requireNonNull(text, "text"); try { if (query == null) { return formatter.parseToBuilder(text, null) .resolve(formatter.getResolverStyle(), formatter.getResolverFields()); } return formatter.parse(text, query); } catch (DateTimeParseException ex) { throw new ParseException(ex.getMessage(), ex.getErrorIndex()); } catch (RuntimeException ex) { throw (ParseException) new ParseException(ex.getMessage(), 0).initCause(ex); } } @Override public Object parseObject(String text, ParsePosition pos) { Jdk8Methods.requireNonNull(text, "text"); Parsed unresolved; try { unresolved = formatter.parseUnresolved0(text, pos); } catch (IndexOutOfBoundsException ex) { if (pos.getErrorIndex() < 0) { pos.setErrorIndex(0); } return null; } if (unresolved == null) { if (pos.getErrorIndex() < 0) { pos.setErrorIndex(0); } return null; } try { DateTimeBuilder builder = unresolved.toBuilder() .resolve(formatter.getResolverStyle(), formatter.getResolverFields()); if (query == null) { return builder; } return builder.build(query); } catch (RuntimeException ex) { pos.setErrorIndex(0); return null; } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy