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

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

/*
 * 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 org.threeten.bp.temporal.ChronoField.DAY_OF_MONTH
import org.threeten.bp.temporal.ChronoField.DAY_OF_WEEK
import org.threeten.bp.temporal.ChronoField.DAY_OF_YEAR
import org.threeten.bp.temporal.ChronoField.HOUR_OF_DAY
import org.threeten.bp.temporal.ChronoField.MINUTE_OF_HOUR
import org.threeten.bp.temporal.ChronoField.MONTH_OF_YEAR
import org.threeten.bp.temporal.ChronoField.NANO_OF_SECOND
import org.threeten.bp.temporal.ChronoField.SECOND_OF_MINUTE
import 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.lang.StringBuilder
import java.util.{ Arrays, Collections, Locale, Objects }
import java.lang.Long

import org.threeten.bp.DateTimeException
import org.threeten.bp.Period
import org.threeten.bp.ZoneId
import org.threeten.bp.chrono.Chronology
import org.threeten.bp.chrono.IsoChronology
import org.threeten.bp.format.internal.TTBPDateTimeFormatterBuilder.CompositePrinterParser
import org.threeten.bp.temporal.IsoFields
import org.threeten.bp.temporal.TemporalAccessor
import org.threeten.bp.temporal.TemporalField
import org.threeten.bp.temporal.TemporalQuery
import org.threeten.bp.format.internal.TTBPDateTimeParseContext
import org.threeten.bp.format.internal.TTBPDateTimePrintContext

object 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.

*/ lazy val ISO_LOCAL_DATE: DateTimeFormatter = 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.

*/ lazy val ISO_OFFSET_DATE: DateTimeFormatter = 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}. */ lazy val ISO_DATE: DateTimeFormatter = 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.

*/ lazy val ISO_LOCAL_TIME: DateTimeFormatter = 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.

*/ lazy val ISO_OFFSET_TIME: DateTimeFormatter = 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}. */ lazy val ISO_TIME: DateTimeFormatter = 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}

*/ lazy val ISO_LOCAL_DATE_TIME: DateTimeFormatter = 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. *

*/ lazy val ISO_OFFSET_DATE_TIME: DateTimeFormatter = 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 ']'.

*/ lazy val ISO_ZONED_DATE_TIME: DateTimeFormatter = 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}. */ lazy val ISO_DATE_TIME: DateTimeFormatter = 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}. */ lazy val ISO_ORDINAL_DATE: DateTimeFormatter = 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}. */ lazy val ISO_WEEK_DATE: DateTimeFormatter = 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 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. */ lazy val ISO_INSTANT: DateTimeFormatter = 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}. */ lazy val BASIC_ISO_DATE: DateTimeFormatter = 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. */ lazy val RFC_1123_DATE_TIME: DateTimeFormatter = { // Size checked val dow: java.util.Map[Long, String] = new java.util.HashMap[Long, String] 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") val moy: java.util.Map[Long, String] = new java.util.HashMap[Long, String] 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") 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) .appendLiteral(' ') .appendValue(HOUR_OF_DAY, 2) .appendLiteral(':') .appendValue(MINUTE_OF_HOUR, 2) .optionalStart() .appendLiteral(':') .appendValue(SECOND_OF_MINUTE, 2) .optionalEnd() .appendLiteral(' ') .appendOffset("+HHMM", "GMT") .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) */ def ofPattern(pattern: String): DateTimeFormatter = 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) */ def ofPattern(pattern: String, locale: Locale): DateTimeFormatter = 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 */ def ofLocalizedDate(dateStyle: FormatStyle): DateTimeFormatter = { Objects.requireNonNull(dateStyle, "dateStyle") 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 */ def ofLocalizedTime(timeStyle: FormatStyle): DateTimeFormatter = { Objects.requireNonNull(timeStyle, "timeStyle") 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 */ def ofLocalizedDateTime(dateTimeStyle: FormatStyle): DateTimeFormatter = { Objects.requireNonNull(dateTimeStyle, "dateTimeStyle") 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 */ def ofLocalizedDateTime(dateStyle: FormatStyle, timeStyle: FormatStyle): DateTimeFormatter = { Objects.requireNonNull(dateStyle, "dateStyle") Objects.requireNonNull(timeStyle, "timeStyle") 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 */ def parsedExcessDays: TemporalQuery[Period] = PARSED_EXCESS_DAYS private lazy val PARSED_EXCESS_DAYS: TemporalQuery[Period] = new TemporalQuery[Period] { override def queryFrom(temporal: TemporalAccessor): Period = temporal match { case builder: DateTimeBuilder => builder.excessDays case _ => 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 */ def parsedLeapSecond: TemporalQuery[Boolean] = PARSED_LEAP_SECOND private lazy val PARSED_LEAP_SECOND: TemporalQuery[Boolean] = new TemporalQuery[Boolean] { override def queryFrom(temporal: TemporalAccessor): Boolean = temporal match { case builder: DateTimeBuilder => builder.leapSecond case _ => false } } /** * Implements the classic Java Format API. * * @param formatter * The formatter * @param query * The query to be parsed */ private[format] class ClassicFormat( private val formatter: DateTimeFormatter, private val query: TemporalQuery[_ <: AnyRef] ) extends Format { def format(obj: AnyRef, toAppendTo: StringBuffer, pos: FieldPosition): StringBuffer = { Objects.requireNonNull(obj, "obj") Objects.requireNonNull(toAppendTo, "toAppendTo") Objects.requireNonNull(pos, "pos") if (!obj.isInstanceOf[TemporalAccessor]) throw new IllegalArgumentException("Format target must implement TemporalAccessor") pos.setBeginIndex(0) pos.setEndIndex(0) try formatter.formatTo(obj.asInstanceOf[TemporalAccessor], toAppendTo) catch { case ex: RuntimeException => throw new IllegalArgumentException(ex.getMessage, ex) } toAppendTo } @throws[ParseException] override def parseObject(text: String): AnyRef = { Objects.requireNonNull(text, "text") try { if (query == null) return formatter .parseToBuilder(text, null) .resolve(formatter.getResolverStyle, formatter.getResolverFields) formatter.parse(text, query) } catch { case ex: DateTimeParseException => throw new ParseException(ex.getMessage, ex.getErrorIndex) case ex: RuntimeException => throw new ParseException(ex.getMessage, 0).initCause(ex).asInstanceOf[ParseException] } } def parseObject(text: String, pos: ParsePosition): AnyRef = { Objects.requireNonNull(text, "text") var unresolved: TTBPDateTimeParseContext#Parsed = null try unresolved = formatter.parseUnresolved0(text, pos) catch { case _: IndexOutOfBoundsException => if (pos.getErrorIndex < 0) pos.setErrorIndex(0) return null } if (unresolved == null) { if (pos.getErrorIndex < 0) pos.setErrorIndex(0) return null } try { val builder: DateTimeBuilder = unresolved.toBuilder.resolve(formatter.getResolverStyle, formatter.getResolverFields) if (query == null) return builder builder.build(query) } catch { case _: RuntimeException => pos.setErrorIndex(0) null } } } } /** * 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. * * @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 */ final class DateTimeFormatter private[format] ( private val printerParser: CompositePrinterParser, private val locale: Locale, private val decimalStyle: DecimalStyle, private val resolverStyle: ResolverStyle, private val resolverFields: java.util.Set[TemporalField], private val chrono: Chronology, private val zone: ZoneId ) { Objects.requireNonNull(printerParser, "printerParser") Objects.requireNonNull(locale, "locale") Objects.requireNonNull(decimalStyle, "decimalStyle") Objects.requireNonNull(resolverStyle, "resolverStyle") /** * 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 */ def getLocale: Locale = 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 */ def withLocale(locale: Locale): DateTimeFormatter = if (this.locale == locale) this else 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 */ def getDecimalStyle: DecimalStyle = 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 */ def withDecimalStyle(decimalStyle: DecimalStyle): DateTimeFormatter = if (this.decimalStyle == decimalStyle) this else 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 */ def getChronology: Chronology = 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 */ def withChronology(chrono: Chronology): DateTimeFormatter = if (Objects.equals(this.chrono, chrono)) this else 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 */ def getZone: ZoneId = 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 */ def withZone(zone: ZoneId): DateTimeFormatter = if (Objects.equals(this.zone, zone)) this else 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 */ def getResolverStyle: ResolverStyle = 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 */ def withResolverStyle(resolverStyle: ResolverStyle): DateTimeFormatter = { Objects.requireNonNull(resolverStyle, "resolverStyle") if (Objects.equals(this.resolverStyle, resolverStyle)) this else 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 */ def getResolverFields: java.util.Set[TemporalField] = 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 */ def withResolverFields(resolverFields: TemporalField*): DateTimeFormatter = if (resolverFields == null) new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, null, chrono, zone) else { val fields: java.util.Set[TemporalField] = new java.util.HashSet[TemporalField](Arrays.asList(resolverFields: _*)) if (Objects.equals(this.resolverFields, fields)) this else new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, Collections.unmodifiableSet(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 */ def withResolverFields(resolverFields: java.util.Set[TemporalField]): DateTimeFormatter = if (resolverFields == null) new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, null, chrono, zone) else if (Objects.equals(this.resolverFields, resolverFields)) this else { val _resolverFields = Collections.unmodifiableSet(new java.util.HashSet[TemporalField](resolverFields)) 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 */ def format(temporal: TemporalAccessor): String = { val buf: StringBuilder = new StringBuilder(32) formatTo(temporal, buf) 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 */ def formatTo(temporal: TemporalAccessor, appendable: Appendable): Unit = { Objects.requireNonNull(temporal, "temporal") Objects.requireNonNull(appendable, "appendable") try { val context: TTBPDateTimePrintContext = new TTBPDateTimePrintContext(temporal, this) if (appendable.isInstanceOf[StringBuilder]) { printerParser.print(context, appendable.asInstanceOf[StringBuilder]) () } else { val buf = new StringBuilder(32) printerParser.print(context, buf) appendable.append(buf) () } } catch { case ex: IOException => 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 */ def parse(text: CharSequence): TemporalAccessor = { Objects.requireNonNull(text, "text") try parseToBuilder(text, null).resolve(resolverStyle, resolverFields) catch { case ex: DateTimeParseException => throw ex case ex: RuntimeException => 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 */ def parse(text: CharSequence, position: ParsePosition): TemporalAccessor = { Objects.requireNonNull(text, "text") Objects.requireNonNull(position, "position") try parseToBuilder(text, position).resolve(resolverStyle, resolverFields) catch { case ex: DateTimeParseException => throw ex case ex: IndexOutOfBoundsException => throw ex case ex: RuntimeException => 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. * * @tparam T * 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 */ def parse[T](text: CharSequence, `type`: TemporalQuery[T]): T = { Objects.requireNonNull(text, "text") Objects.requireNonNull(`type`, "type") try { val builder: DateTimeBuilder = parseToBuilder(text, null).resolve(resolverStyle, resolverFields) builder.build(`type`) } catch { case ex: DateTimeParseException => throw ex case ex: RuntimeException => 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 */ @scala.annotation.varargs def parseBest(text: CharSequence, types: TemporalQuery[_]*): TemporalAccessor = { Objects.requireNonNull(text, "text") Objects.requireNonNull(types, "types") if (types.length < 2) throw new IllegalArgumentException("At least two types must be specified") try { val builder: DateTimeBuilder = parseToBuilder(text, null).resolve(resolverStyle, resolverFields) for (tpe <- types) try return builder.build(tpe).asInstanceOf[TemporalAccessor] catch { case _: RuntimeException => } throw new DateTimeException( s"Unable to convert parsed text to any specified type: ${types.mkString("[", ", ", "]")}" ) } catch { case ex: DateTimeParseException => throw ex case ex: RuntimeException => throw createError(text, ex) } } private def createError(text: CharSequence, ex: RuntimeException): DateTimeParseException = { val abbr = if (text.length > 64) text.subSequence(0, 64).toString + "..." else text.toString new DateTimeParseException(s"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 def parseToBuilder(text: CharSequence, position: ParsePosition): DateTimeBuilder = { val pos: ParsePosition = if (position != null) position else new ParsePosition(0) val result: TTBPDateTimeParseContext#Parsed = parseUnresolved0(text, pos) if ( result == null || pos.getErrorIndex >= 0 || (position == null && pos.getIndex < text.length) ) { val abbr = if (text.length > 64) text.subSequence(0, 64).toString + "..." else text.toString if (pos.getErrorIndex >= 0) throw new DateTimeParseException( s"Text '$abbr' could not be parsed at index ${pos.getErrorIndex}", text, pos.getErrorIndex ) else throw new DateTimeParseException( s"Text '$abbr' could not be parsed, unparsed text found at index ${pos.getIndex}", text, pos.getIndex ) } 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 */ def parseUnresolved(text: CharSequence, position: ParsePosition): TemporalAccessor = parseUnresolved0(text, position) private def parseUnresolved0( text: CharSequence, position: ParsePosition ): TTBPDateTimeParseContext#Parsed = { Objects.requireNonNull(text, "text") Objects.requireNonNull(position, "position") val context: TTBPDateTimeParseContext = new TTBPDateTimeParseContext(this) val pos = printerParser.parse(context, text, position.getIndex()) if (pos < 0) { position.setErrorIndex(~pos) null } else { position.setIndex(pos) 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 */ private[format] def toPrinterParser(optional: Boolean): CompositePrinterParser = 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 */ def toFormat: Format = new DateTimeFormatter.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 */ def toFormat(query: TemporalQuery[_ <: AnyRef]): Format = { Objects.requireNonNull(query, "query") new DateTimeFormatter.ClassicFormat(this, query) } /** * Returns a description of the underlying formatters. * * @return * a description of this formatter, not null */ override def toString: String = { val pattern: String = printerParser.toString if (pattern.startsWith("[")) pattern else pattern.substring(1, pattern.length - 1) } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy