--- /dev/null 2013-01-18 16:17:08.886776012 -0800 +++ new/src/share/classes/java/time/format/DateTimeFormatters.java 2013-01-22 16:57:58.000000000 -0800 @@ -0,0 +1,972 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, 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 java.time.format; + +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static java.time.temporal.ChronoField.DAY_OF_YEAR; +import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.NANO_OF_SECOND; +import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; +import static java.time.temporal.ChronoField.YEAR; + +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.temporal.ChronoField; +import java.time.temporal.ISOFields; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; + +/** + * Provides common implementations of {@code DateTimeFormatter}. + *

+ * This utility class provides three different ways to obtain a formatter. + *

+ * + *

Specification for implementors

+ * This is a thread-safe utility class. + * All returned formatters are immutable and thread-safe. + * + * @since 1.8 + */ +public final class DateTimeFormatters { + + /** + * Private constructor since this is a utility class. + */ + private DateTimeFormatters() { + } + + //----------------------------------------------------------------------- + /** + * 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
+     *
+     *   I       time-zone ID                zoneId            America/Los_Angeles
+     *   z       time-zone name              text              Pacific Standard Time; PST
+     *   Z       zone-offset                 offset-Z          +0000; -0800; -08:00;
+     *   X       zone-offset 'Z' for zero    offset-X          Z; -08; -0830; -08:30; -083015; -08:30:15;
+     *
+     *   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(java.time.temporal.TemporalField)}. + * Otherwise, the count of digits is used as the width of the output field as per + * {@link DateTimeFormatterBuilder#appendValue(java.time.temporal.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: 'I' outputs the zone ID, such as 'Europe/Paris'. + *

+ * Offset X: This formats the offset using 'Z' when the offset is zero. + * One letter outputs just the hour', such as '+01' + * 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'. + *

+ * Offset Z: This formats the offset using '+0000' or '+00:00' when the offset is zero. + * One or 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'. + *

+ * Zone names: Time zone names ('z') cannot be parsed. + *

+ * 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. + *

+ * The pattern string is similar, but not identical, to {@link java.text.SimpleDateFormat SimpleDateFormat}. + * Pattern letters 'E' and 'u' are merged, which changes the meaning of "E" and "EE" to be numeric. + * Pattern letters 'Z' and 'X' are extended. + * Pattern letter 'y' and 'Y' parse years of two digits and more than 4 digits differently. + * Pattern letters 'n', 'A', 'N', 'I' and 'p' are added. + * Number types will reject large numbers. + * The pattern string is also similar, but not identical, to that defined by the + * Unicode Common Locale Data Repository (CLDR). + * + * @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 pattern(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 #pattern(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 pattern(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(Locale.Category) default FORMAT 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 localizedDate(FormatStyle dateStyle) { + Objects.requireNonNull(dateStyle, "dateStyle"); + return new DateTimeFormatterBuilder().appendLocalized(dateStyle, null).toFormatter(); + } + + /** + * 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(Locale.Category) default FORMAT 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 localizedTime(FormatStyle timeStyle) { + Objects.requireNonNull(timeStyle, "timeStyle"); + return new DateTimeFormatterBuilder().appendLocalized(null, timeStyle).toFormatter(); + } + + /** + * 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(Locale.Category) default FORMAT 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 localizedDateTime(FormatStyle dateTimeStyle) { + Objects.requireNonNull(dateTimeStyle, "dateTimeStyle"); + return new DateTimeFormatterBuilder().appendLocalized(dateTimeStyle, dateTimeStyle).toFormatter(); + } + + /** + * 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 FORMAT 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 localizedDateTime(FormatStyle dateStyle, FormatStyle timeStyle) { + Objects.requireNonNull(dateStyle, "dateStyle"); + Objects.requireNonNull(timeStyle, "timeStyle"); + return new DateTimeFormatterBuilder().appendLocalized(dateStyle, timeStyle).toFormatter(); + } + + //----------------------------------------------------------------------- + /** + * 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: + *

+ * + * @return the ISO local date formatter, not null + */ + public static DateTimeFormatter isoLocalDate() { + return ISO_LOCAL_DATE; + } + + /** Singleton date formatter. */ + private 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(); + } + + //----------------------------------------------------------------------- + /** + * 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: + *

+ * + * @return the ISO offset date formatter, not null + */ + public static DateTimeFormatter isoOffsetDate() { + return ISO_OFFSET_DATE; + } + + /** Singleton date formatter. */ + private static final DateTimeFormatter ISO_OFFSET_DATE; + static { + ISO_OFFSET_DATE = new DateTimeFormatterBuilder() + .parseCaseInsensitive() + .append(ISO_LOCAL_DATE) + .appendOffsetId() + .toFormatter(); + } + + //----------------------------------------------------------------------- + /** + * 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: + *

+ * As this formatter has an optional element, it may be necessary to parse using + * {@link DateTimeFormatter#parseBest}. + * + * @return the ISO date formatter, not null + */ + public static DateTimeFormatter isoDate() { + return ISO_DATE; + } + + /** Singleton date formatter. */ + private static final DateTimeFormatter ISO_DATE; + static { + ISO_DATE = new DateTimeFormatterBuilder() + .parseCaseInsensitive() + .append(ISO_LOCAL_DATE) + .optionalStart() + .appendOffsetId() + .toFormatter(); + } + + //----------------------------------------------------------------------- + /** + * 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: + *

+ * + * @return the ISO local time formatter, not null + */ + public static DateTimeFormatter isoLocalTime() { + return ISO_LOCAL_TIME; + } + + /** Singleton date formatter. */ + private 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(); + } + + //----------------------------------------------------------------------- + /** + * 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: + *

+ * + * @return the ISO offset time formatter, not null + */ + public static DateTimeFormatter isoOffsetTime() { + return ISO_OFFSET_TIME; + } + + /** Singleton date formatter. */ + private static final DateTimeFormatter ISO_OFFSET_TIME; + static { + ISO_OFFSET_TIME = new DateTimeFormatterBuilder() + .parseCaseInsensitive() + .append(ISO_LOCAL_TIME) + .appendOffsetId() + .toFormatter(); + } + + //----------------------------------------------------------------------- + /** + * 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: + *

+ * As this formatter has an optional element, it may be necessary to parse using + * {@link DateTimeFormatter#parseBest}. + * + * @return the ISO time formatter, not null + */ + public static DateTimeFormatter isoTime() { + return ISO_TIME; + } + + /** Singleton date formatter. */ + private static final DateTimeFormatter ISO_TIME; + static { + ISO_TIME = new DateTimeFormatterBuilder() + .parseCaseInsensitive() + .append(ISO_LOCAL_TIME) + .optionalStart() + .appendOffsetId() + .toFormatter(); + } + + //----------------------------------------------------------------------- + /** + * 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: + *

+ * + * @return the ISO local date-time formatter, not null + */ + public static DateTimeFormatter isoLocalDateTime() { + return ISO_LOCAL_DATE_TIME; + } + + /** Singleton date formatter. */ + private 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(); + } + + //----------------------------------------------------------------------- + /** + * 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: + *

+ * + * @return the ISO offset date-time formatter, not null + */ + public static DateTimeFormatter isoOffsetDateTime() { + return ISO_OFFSET_DATE_TIME; + } + + /** Singleton date formatter. */ + private static final DateTimeFormatter ISO_OFFSET_DATE_TIME; + static { + ISO_OFFSET_DATE_TIME = new DateTimeFormatterBuilder() + .parseCaseInsensitive() + .append(ISO_LOCAL_DATE_TIME) + .appendOffsetId() + .toFormatter(); + } + + //----------------------------------------------------------------------- + /** + * 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: + *

+ * + * @return the ISO zoned date-time formatter, not null + */ + public static DateTimeFormatter isoZonedDateTime() { + return ISO_ZONED_DATE_TIME; + } + + /** Singleton date formatter. */ + private 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(); + } + + //----------------------------------------------------------------------- + /** + * 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: + *

+ * As this formatter has an optional element, it may be necessary to parse using + * {@link DateTimeFormatter#parseBest}. + * + * @return the ISO date-time formatter, not null + */ + public static DateTimeFormatter isoDateTime() { + return ISO_DATE_TIME; + } + + /** Singleton date formatter. */ + private 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(); + } + + //----------------------------------------------------------------------- + /** + * 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: + *

+ * As this formatter has an optional element, it may be necessary to parse using + * {@link DateTimeFormatter#parseBest}. + * + * @return the ISO ordinal date formatter, not null + */ + public static DateTimeFormatter isoOrdinalDate() { + return ISO_ORDINAL_DATE; + } + + /** Singleton date formatter. */ + private 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(); + } + + //----------------------------------------------------------------------- + /** + * 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: + *

+ * As this formatter has an optional element, it may be necessary to parse using + * {@link DateTimeFormatter#parseBest}. + * + * @return the ISO week-based date formatter, not null + */ + public static DateTimeFormatter isoWeekDate() { + return ISO_WEEK_DATE; + } + + /** Singleton date formatter. */ + private 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(); + } + + //----------------------------------------------------------------------- + /** + * Returns the ISO instant formatter that prints/parses an instant in UTC. + *

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

+ * + * @return the ISO instant formatter, not null + */ + public static DateTimeFormatter isoInstant() { + return ISO_INSTANT; + } + + /** Singleton formatter. */ + private static final DateTimeFormatter ISO_INSTANT; + static { + ISO_INSTANT = new DateTimeFormatterBuilder() + .parseCaseInsensitive() + .appendInstant() + .toFormatter(); + } + + //----------------------------------------------------------------------- + /** + * 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: + *

+ * As this formatter has an optional element, it may be necessary to parse using + * {@link DateTimeFormatter#parseBest}. + * + * @return the ISO basic local date formatter, not null + */ + public static DateTimeFormatter basicIsoDate() { + return BASIC_ISO_DATE; + } + + /** Singleton date formatter. */ + private 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(); + } + + //----------------------------------------------------------------------- + /** + * 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: + *

+ * Parsing is case insensitive. + * + * @return the RFC-1123 formatter, not null + */ + public static DateTimeFormatter rfc1123() { + return RFC_1123_DATE_TIME; + } + + /** Singleton date formatter. */ + private 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(); + } + +}