jdk/src/share/classes/java/time/format/DateTimeFormatterBuilder.java
changeset 15658 55b829ca2334
parent 15289 3ac550392e43
child 16005 b480157c22fe
equal deleted inserted replaced
15657:c588664d547e 15658:55b829ca2334
    79 import java.time.DateTimeException;
    79 import java.time.DateTimeException;
    80 import java.time.Instant;
    80 import java.time.Instant;
    81 import java.time.LocalDateTime;
    81 import java.time.LocalDateTime;
    82 import java.time.ZoneId;
    82 import java.time.ZoneId;
    83 import java.time.ZoneOffset;
    83 import java.time.ZoneOffset;
       
    84 import java.time.chrono.Chronology;
       
    85 import java.time.chrono.IsoChronology;
       
    86 import java.time.chrono.JapaneseChronology;
    84 import java.time.format.DateTimeTextProvider.LocaleStore;
    87 import java.time.format.DateTimeTextProvider.LocaleStore;
    85 import java.time.temporal.Chrono;
       
    86 import java.time.temporal.ChronoField;
    88 import java.time.temporal.ChronoField;
    87 import java.time.temporal.ISOChrono;
    89 import java.time.temporal.IsoFields;
    88 import java.time.temporal.ISOFields;
       
    89 import java.time.temporal.Queries;
    90 import java.time.temporal.Queries;
    90 import java.time.temporal.TemporalAccessor;
    91 import java.time.temporal.TemporalAccessor;
    91 import java.time.temporal.TemporalField;
    92 import java.time.temporal.TemporalField;
    92 import java.time.temporal.TemporalQuery;
    93 import java.time.temporal.TemporalQuery;
    93 import java.time.temporal.ValueRange;
    94 import java.time.temporal.ValueRange;
    97 import java.util.ArrayList;
    98 import java.util.ArrayList;
    98 import java.util.Arrays;
    99 import java.util.Arrays;
    99 import java.util.Collections;
   100 import java.util.Collections;
   100 import java.util.Comparator;
   101 import java.util.Comparator;
   101 import java.util.HashMap;
   102 import java.util.HashMap;
       
   103 import java.util.HashSet;
   102 import java.util.Iterator;
   104 import java.util.Iterator;
   103 import java.util.LinkedHashMap;
   105 import java.util.LinkedHashMap;
   104 import java.util.List;
   106 import java.util.List;
   105 import java.util.Locale;
   107 import java.util.Locale;
   106 import java.util.Map;
   108 import java.util.Map;
   287     //-----------------------------------------------------------------------
   289     //-----------------------------------------------------------------------
   288     /**
   290     /**
   289      * Appends the value of a date-time field to the formatter using a normal
   291      * Appends the value of a date-time field to the formatter using a normal
   290      * output style.
   292      * output style.
   291      * <p>
   293      * <p>
   292      * The value of the field will be output during a print.
   294      * The value of the field will be output during a format.
   293      * If the value cannot be obtained then an exception will be thrown.
   295      * If the value cannot be obtained then an exception will be thrown.
   294      * <p>
   296      * <p>
   295      * The value will be printed as per the normal print of an integer value.
   297      * The value will be printed as per the normal format of an integer value.
   296      * Only negative numbers will be signed. No padding will be added.
   298      * Only negative numbers will be signed. No padding will be added.
   297      * <p>
   299      * <p>
   298      * The parser for a variable width value such as this normally behaves greedily,
   300      * The parser for a variable width value such as this normally behaves greedily,
   299      * requiring one digit, but accepting as many digits as possible.
   301      * requiring one digit, but accepting as many digits as possible.
   300      * This behavior can be affected by 'adjacent value parsing'.
   302      * This behavior can be affected by 'adjacent value parsing'.
   311 
   313 
   312     /**
   314     /**
   313      * Appends the value of a date-time field to the formatter using a fixed
   315      * Appends the value of a date-time field to the formatter using a fixed
   314      * width, zero-padded approach.
   316      * width, zero-padded approach.
   315      * <p>
   317      * <p>
   316      * The value of the field will be output during a print.
   318      * The value of the field will be output during a format.
   317      * If the value cannot be obtained then an exception will be thrown.
   319      * If the value cannot be obtained then an exception will be thrown.
   318      * <p>
   320      * <p>
   319      * The value will be zero-padded on the left. If the size of the value
   321      * The value will be zero-padded on the left. If the size of the value
   320      * means that it cannot be printed within the width then an exception is thrown.
   322      * means that it cannot be printed within the width then an exception is thrown.
   321      * If the value of the field is negative then an exception is thrown during printing.
   323      * If the value of the field is negative then an exception is thrown during formatting.
   322      * <p>
   324      * <p>
   323      * This method supports a special technique of parsing known as 'adjacent value parsing'.
   325      * This method supports a special technique of parsing known as 'adjacent value parsing'.
   324      * This technique solves the problem where a variable length value is followed by one or more
   326      * This technique solves the problem where a variable length value is followed by one or more
   325      * fixed length values. The standard parser is greedy, and thus it would normally
   327      * fixed length values. The standard parser is greedy, and thus it would normally
   326      * steal the digits that are needed by the fixed width value parsers that follow the
   328      * steal the digits that are needed by the fixed width value parsers that follow the
   366         return appendFixedWidth(width, pp);
   368         return appendFixedWidth(width, pp);
   367     }
   369     }
   368 
   370 
   369     /**
   371     /**
   370      * Appends the value of a date-time field to the formatter providing full
   372      * Appends the value of a date-time field to the formatter providing full
   371      * control over printing.
   373      * control over formatting.
   372      * <p>
   374      * <p>
   373      * The value of the field will be output during a print.
   375      * The value of the field will be output during a format.
   374      * If the value cannot be obtained then an exception will be thrown.
   376      * If the value cannot be obtained then an exception will be thrown.
   375      * <p>
   377      * <p>
   376      * This method provides full control of the numeric formatting, including
   378      * This method provides full control of the numeric formatting, including
   377      * zero-padding and the positive/negative sign.
   379      * zero-padding and the positive/negative sign.
   378      * <p>
   380      * <p>
   384      * In strict parsing mode, the minimum number of parsed digits is {@code minWidth}.
   386      * In strict parsing mode, the minimum number of parsed digits is {@code minWidth}.
   385      * In lenient parsing mode, the minimum number of parsed digits is one.
   387      * In lenient parsing mode, the minimum number of parsed digits is one.
   386      * <p>
   388      * <p>
   387      * If this method is invoked with equal minimum and maximum widths and a sign style of
   389      * If this method is invoked with equal minimum and maximum widths and a sign style of
   388      * {@code NOT_NEGATIVE} then it delegates to {@code appendValue(TemporalField,int)}.
   390      * {@code NOT_NEGATIVE} then it delegates to {@code appendValue(TemporalField,int)}.
   389      * In this scenario, the printing and parsing behavior described there occur.
   391      * In this scenario, the formatting and parsing behavior described there occur.
   390      *
   392      *
   391      * @param field  the field to append, not null
   393      * @param field  the field to append, not null
   392      * @param minWidth  the minimum field width of the printed field, from 1 to 19
   394      * @param minWidth  the minimum field width of the printed field, from 1 to 19
   393      * @param maxWidth  the maximum field width of the printed field, from 1 to 19
   395      * @param maxWidth  the maximum field width of the printed field, from 1 to 19
   394      * @param signStyle  the positive/negative output style, not null
   396      * @param signStyle  the positive/negative output style, not null
   423 
   425 
   424     //-----------------------------------------------------------------------
   426     //-----------------------------------------------------------------------
   425     /**
   427     /**
   426      * Appends the reduced value of a date-time field to the formatter.
   428      * Appends the reduced value of a date-time field to the formatter.
   427      * <p>
   429      * <p>
   428      * This is typically used for printing and parsing a two digit year.
   430      * This is typically used for formatting and parsing a two digit year.
   429      * The {@code width} is the printed and parsed width.
   431      * The {@code width} is the printed and parsed width.
   430      * The {@code baseValue} is used during parsing to determine the valid range.
   432      * The {@code baseValue} is used during parsing to determine the valid range.
   431      * <p>
   433      * <p>
   432      * For printing, the width is used to determine the number of characters to print.
   434      * For formatting, the width is used to determine the number of characters to format.
   433      * The rightmost characters are output to match the width, left padding with zero.
   435      * The rightmost characters are output to match the width, left padding with zero.
   434      * <p>
   436      * <p>
   435      * For parsing, exactly the number of characters specified by the width are parsed.
   437      * For parsing, exactly the number of characters specified by the width are parsed.
   436      * This is incomplete information however, so the base value is used to complete the parse.
   438      * This is incomplete information however, so the base value is used to complete the parse.
   437      * The base value is the first valid value in a range of ten to the power of width.
   439      * The base value is the first valid value in a range of ten to the power of width.
   523     //-----------------------------------------------------------------------
   525     //-----------------------------------------------------------------------
   524     /**
   526     /**
   525      * Appends the text of a date-time field to the formatter using the full
   527      * Appends the text of a date-time field to the formatter using the full
   526      * text style.
   528      * text style.
   527      * <p>
   529      * <p>
   528      * The text of the field will be output during a print.
   530      * The text of the field will be output during a format.
   529      * The value must be within the valid range of the field.
   531      * The value must be within the valid range of the field.
   530      * If the value cannot be obtained then an exception will be thrown.
   532      * If the value cannot be obtained then an exception will be thrown.
   531      * If the field has no textual representation, then the numeric value will be used.
   533      * If the field has no textual representation, then the numeric value will be used.
   532      * <p>
   534      * <p>
   533      * The value will be printed as per the normal print of an integer value.
   535      * The value will be printed as per the normal format of an integer value.
   534      * Only negative numbers will be signed. No padding will be added.
   536      * Only negative numbers will be signed. No padding will be added.
   535      *
   537      *
   536      * @param field  the field to append, not null
   538      * @param field  the field to append, not null
   537      * @return this, for chaining, not null
   539      * @return this, for chaining, not null
   538      */
   540      */
   541     }
   543     }
   542 
   544 
   543     /**
   545     /**
   544      * Appends the text of a date-time field to the formatter.
   546      * Appends the text of a date-time field to the formatter.
   545      * <p>
   547      * <p>
   546      * The text of the field will be output during a print.
   548      * The text of the field will be output during a format.
   547      * The value must be within the valid range of the field.
   549      * The value must be within the valid range of the field.
   548      * If the value cannot be obtained then an exception will be thrown.
   550      * If the value cannot be obtained then an exception will be thrown.
   549      * If the field has no textual representation, then the numeric value will be used.
   551      * If the field has no textual representation, then the numeric value will be used.
   550      * <p>
   552      * <p>
   551      * The value will be printed as per the normal print of an integer value.
   553      * The value will be printed as per the normal format of an integer value.
   552      * Only negative numbers will be signed. No padding will be added.
   554      * Only negative numbers will be signed. No padding will be added.
   553      *
   555      *
   554      * @param field  the field to append, not null
   556      * @param field  the field to append, not null
   555      * @param textStyle  the text style to use, not null
   557      * @param textStyle  the text style to use, not null
   556      * @return this, for chaining, not null
   558      * @return this, for chaining, not null
   566      * Appends the text of a date-time field to the formatter using the specified
   568      * Appends the text of a date-time field to the formatter using the specified
   567      * map to supply the text.
   569      * map to supply the text.
   568      * <p>
   570      * <p>
   569      * The standard text outputting methods use the localized text in the JDK.
   571      * The standard text outputting methods use the localized text in the JDK.
   570      * This method allows that text to be specified directly.
   572      * This method allows that text to be specified directly.
   571      * The supplied map is not validated by the builder to ensure that printing or
   573      * The supplied map is not validated by the builder to ensure that formatting or
   572      * parsing is possible, thus an invalid map may throw an error during later use.
   574      * parsing is possible, thus an invalid map may throw an error during later use.
   573      * <p>
   575      * <p>
   574      * Supplying the map of text provides considerable flexibility in printing and parsing.
   576      * Supplying the map of text provides considerable flexibility in formatting and parsing.
   575      * For example, a legacy application might require or supply the months of the
   577      * For example, a legacy application might require or supply the months of the
   576      * year as "JNY", "FBY", "MCH" etc. These do not match the standard set of text
   578      * year as "JNY", "FBY", "MCH" etc. These do not match the standard set of text
   577      * for localized month names. Using this method, a map can be created which
   579      * for localized month names. Using this method, a map can be created which
   578      * defines the connection between each value and the text:
   580      * defines the connection between each value and the text:
   579      * <pre>
   581      * <pre>
   586      * </pre>
   588      * </pre>
   587      * <p>
   589      * <p>
   588      * Other uses might be to output the value with a suffix, such as "1st", "2nd", "3rd",
   590      * Other uses might be to output the value with a suffix, such as "1st", "2nd", "3rd",
   589      * or as Roman numerals "I", "II", "III", "IV".
   591      * or as Roman numerals "I", "II", "III", "IV".
   590      * <p>
   592      * <p>
   591      * During printing, the value is obtained and checked that it is in the valid range.
   593      * During formatting, the value is obtained and checked that it is in the valid range.
   592      * If text is not available for the value then it is output as a number.
   594      * If text is not available for the value then it is output as a number.
   593      * During parsing, the parser will match against the map of text and numeric values.
   595      * During parsing, the parser will match against the map of text and numeric values.
   594      *
   596      *
   595      * @param field  the field to append, not null
   597      * @param field  the field to append, not null
   596      * @param textLookup  the map from the value to the text
   598      * @param textLookup  the map from the value to the text
   622      * <p>
   624      * <p>
   623      * Instants have a fixed output format.
   625      * Instants have a fixed output format.
   624      * They are converted to a date-time with a zone-offset of UTC and printed
   626      * They are converted to a date-time with a zone-offset of UTC and printed
   625      * using the standard ISO-8601 format.
   627      * using the standard ISO-8601 format.
   626      * <p>
   628      * <p>
   627      * An alternative to this method is to print/parse the instant as a single
   629      * An alternative to this method is to format/parse the instant as a single
   628      * epoch-seconds value. That is achieved using {@code appendValue(INSTANT_SECONDS)}.
   630      * epoch-seconds value. That is achieved using {@code appendValue(INSTANT_SECONDS)}.
   629      *
   631      *
   630      * @return this, for chaining, not null
   632      * @return this, for chaining, not null
   631      */
   633      */
   632     public DateTimeFormatterBuilder appendInstant() {
   634     public DateTimeFormatterBuilder appendInstant() {
   635     }
   637     }
   636 
   638 
   637     /**
   639     /**
   638      * Appends the zone offset, such as '+01:00', to the formatter.
   640      * Appends the zone offset, such as '+01:00', to the formatter.
   639      * <p>
   641      * <p>
   640      * This appends an instruction to print/parse the offset ID to the builder.
   642      * This appends an instruction to format/parse the offset ID to the builder.
   641      * This is equivalent to calling {@code appendOffset("HH:MM:ss", "Z")}.
   643      * This is equivalent to calling {@code appendOffset("HH:MM:ss", "Z")}.
   642      *
   644      *
   643      * @return this, for chaining, not null
   645      * @return this, for chaining, not null
   644      */
   646      */
   645     public DateTimeFormatterBuilder appendOffsetId() {
   647     public DateTimeFormatterBuilder appendOffsetId() {
   646         appendInternal(OffsetIdPrinterParser.INSTANCE_ID);
   648         appendInternal(OffsetIdPrinterParser.INSTANCE_ID_Z);
   647         return this;
   649         return this;
   648     }
   650     }
   649 
   651 
   650     /**
   652     /**
   651      * Appends the zone offset, such as '+01:00', to the formatter.
   653      * Appends the zone offset, such as '+01:00', to the formatter.
   652      * <p>
   654      * <p>
   653      * This appends an instruction to print/parse the offset ID to the builder.
   655      * This appends an instruction to format/parse the offset ID to the builder.
   654      * <p>
   656      * <p>
   655      * During printing, the offset is obtained using a mechanism equivalent
   657      * During formatting, the offset is obtained using a mechanism equivalent
   656      * to querying the temporal with {@link Queries#offset()}.
   658      * to querying the temporal with {@link Queries#offset()}.
   657      * It will be printed using the format defined below.
   659      * It will be printed using the format defined below.
   658      * If the offset cannot be obtained then an exception is thrown unless the
   660      * If the offset cannot be obtained then an exception is thrown unless the
   659      * section of the formatter is optional.
   661      * section of the formatter is optional.
   660      * <p>
   662      * <p>
   663      * section of the formatter is optional.
   665      * section of the formatter is optional.
   664      * <p>
   666      * <p>
   665      * The format of the offset is controlled by a pattern which must be one
   667      * The format of the offset is controlled by a pattern which must be one
   666      * of the following:
   668      * of the following:
   667      * <p><ul>
   669      * <p><ul>
   668      * <li>{@code +HH} - hour only, ignoring any minute
   670      * <li>{@code +HH} - hour only, ignoring minute and second
   669      * <li>{@code +HHMM} - hour and minute, no colon
   671      * <li>{@code +HHmm} - hour, with minute if non-zero, ignoring second, no colon
   670      * <li>{@code +HH:MM} - hour and minute, with colon
   672      * <li>{@code +HH:mm} - hour, with minute if non-zero, ignoring second, with colon
   671      * <li>{@code +HHMMss} - hour and minute, with second if non-zero and no colon
   673      * <li>{@code +HHMM} - hour and minute, ignoring second, no colon
   672      * <li>{@code +HH:MM:ss} - hour and minute, with second if non-zero and colon
   674      * <li>{@code +HH:MM} - hour and minute, ignoring second, with colon
       
   675      * <li>{@code +HHMMss} - hour and minute, with second if non-zero, no colon
       
   676      * <li>{@code +HH:MM:ss} - hour and minute, with second if non-zero, with colon
   673      * <li>{@code +HHMMSS} - hour, minute and second, no colon
   677      * <li>{@code +HHMMSS} - hour, minute and second, no colon
   674      * <li>{@code +HH:MM:SS} - hour, minute and second, with colon
   678      * <li>{@code +HH:MM:SS} - hour, minute and second, with colon
   675      * </ul><p>
   679      * </ul><p>
   676      * The "no offset" text controls what text is printed when the offset is zero.
   680      * The "no offset" text controls what text is printed when the total amount of
       
   681      * the offset fields to be output is zero.
   677      * Example values would be 'Z', '+00:00', 'UTC' or 'GMT'.
   682      * Example values would be 'Z', '+00:00', 'UTC' or 'GMT'.
   678      * Three formats are accepted for parsing UTC - the "no offset" text, and the
   683      * Three formats are accepted for parsing UTC - the "no offset" text, and the
   679      * plus and minus versions of zero defined by the pattern.
   684      * plus and minus versions of zero defined by the pattern.
   680      *
   685      *
   681      * @param pattern  the pattern to use, not null
   686      * @param pattern  the pattern to use, not null
   682      * @param noOffsetText  the text to use when the offset is zero, not null
   687      * @param noOffsetText  the text to use when the offset is zero, not null
   683      * @return this, for chaining, not null
   688      * @return this, for chaining, not null
   684      */
   689      */
   685     public DateTimeFormatterBuilder appendOffset(String pattern, String noOffsetText) {
   690     public DateTimeFormatterBuilder appendOffset(String pattern, String noOffsetText) {
   686         appendInternal(new OffsetIdPrinterParser(noOffsetText, pattern));
   691         appendInternal(new OffsetIdPrinterParser(pattern, noOffsetText));
   687         return this;
   692         return this;
   688     }
   693     }
   689 
   694 
   690     //-----------------------------------------------------------------------
   695     //-----------------------------------------------------------------------
   691     /**
   696     /**
   692      * Appends the time-zone ID, such as 'Europe/Paris' or '+02:00', to the formatter.
   697      * Appends the time-zone ID, such as 'Europe/Paris' or '+02:00', to the formatter.
   693      * <p>
   698      * <p>
   694      * This appends an instruction to print/parse the zone ID to the builder.
   699      * This appends an instruction to format/parse the zone ID to the builder.
   695      * The zone ID is obtained in a strict manner suitable for {@code ZonedDateTime}.
   700      * The zone ID is obtained in a strict manner suitable for {@code ZonedDateTime}.
   696      * By contrast, {@code OffsetDateTime} does not have a zone ID suitable
   701      * By contrast, {@code OffsetDateTime} does not have a zone ID suitable
   697      * for use with this method, see {@link #appendZoneOrOffsetId()}.
   702      * for use with this method, see {@link #appendZoneOrOffsetId()}.
   698      * <p>
   703      * <p>
   699      * During printing, the zone is obtained using a mechanism equivalent
   704      * During formatting, the zone is obtained using a mechanism equivalent
   700      * to querying the temporal with {@link Queries#zoneId()}.
   705      * to querying the temporal with {@link Queries#zoneId()}.
   701      * It will be printed using the result of {@link ZoneId#getId()}.
   706      * It will be printed using the result of {@link ZoneId#getId()}.
   702      * If the zone cannot be obtained then an exception is thrown unless the
   707      * If the zone cannot be obtained then an exception is thrown unless the
   703      * section of the formatter is optional.
   708      * section of the formatter is optional.
   704      * <p>
   709      * <p>
   705      * During parsing, the zone is parsed and must match a known zone or offset.
   710      * During parsing, the text must match a known zone or offset.
   706      * If the zone cannot be parsed then an exception is thrown unless the
   711      * There are two types of zone ID, offset-based, such as '+01:30' and
   707      * section of the formatter is optional.
   712      * region-based, such as 'Europe/London'. These are parsed differently.
       
   713      * If the parse starts with '+', '-', 'UT', 'UTC' or 'GMT', then the parser
       
   714      * expects an offset-based zone and will not match region-based zones.
       
   715      * The offset ID, such as '+02:30', may be at the start of the parse,
       
   716      * or prefixed by  'UT', 'UTC' or 'GMT'. The offset ID parsing is
       
   717      * equivalent to using {@link #appendOffset(String, String)} using the
       
   718      * arguments 'HH:MM:ss' and the no offset string '0'.
       
   719      * If the parse starts with 'UT', 'UTC' or 'GMT', and the parser cannot
       
   720      * match a following offset ID, then {@link ZoneOffset#UTC} is selected.
       
   721      * In all other cases, the list of known region-based zones is used to
       
   722      * find the longest available match. If no match is found, and the parse
       
   723      * starts with 'Z', then {@code ZoneOffset.UTC} is selected.
       
   724      * The parser uses the {@linkplain #parseCaseInsensitive() case sensitive} setting.
       
   725      * <p>
       
   726      * For example, the following will parse:
       
   727      * <pre>
       
   728      *   "Europe/London"           -> ZoneId.of("Europe/London")
       
   729      *   "Z"                       -> ZoneOffset.UTC
       
   730      *   "UT"                      -> ZoneOffset.UTC
       
   731      *   "UTC"                     -> ZoneOffset.UTC
       
   732      *   "GMT"                     -> ZoneOffset.UTC
       
   733      *   "UT0"                     -> ZoneOffset.UTC
       
   734      *   "UTC0"                    -> ZoneOffset.UTC
       
   735      *   "GMT0"                    -> ZoneOffset.UTC
       
   736      *   "+01:30"                  -> ZoneOffset.of("+01:30")
       
   737      *   "UT+01:30"                -> ZoneOffset.of("+01:30")
       
   738      *   "UTC+01:30"               -> ZoneOffset.of("+01:30")
       
   739      *   "GMT+01:30"               -> ZoneOffset.of("+01:30")
       
   740      * </pre>
   708      *
   741      *
   709      * @return this, for chaining, not null
   742      * @return this, for chaining, not null
   710      * @see #appendZoneRegionId()
   743      * @see #appendZoneRegionId()
   711      */
   744      */
   712     public DateTimeFormatterBuilder appendZoneId() {
   745     public DateTimeFormatterBuilder appendZoneId() {
   716 
   749 
   717     /**
   750     /**
   718      * Appends the time-zone region ID, such as 'Europe/Paris', to the formatter,
   751      * Appends the time-zone region ID, such as 'Europe/Paris', to the formatter,
   719      * rejecting the zone ID if it is a {@code ZoneOffset}.
   752      * rejecting the zone ID if it is a {@code ZoneOffset}.
   720      * <p>
   753      * <p>
   721      * This appends an instruction to print/parse the zone ID to the builder
   754      * This appends an instruction to format/parse the zone ID to the builder
   722      * only if it is a region-based ID.
   755      * only if it is a region-based ID.
   723      * <p>
   756      * <p>
   724      * During printing, the zone is obtained using a mechanism equivalent
   757      * During formatting, the zone is obtained using a mechanism equivalent
   725      * to querying the temporal with {@link Queries#zoneId()}.
   758      * to querying the temporal with {@link Queries#zoneId()}.
   726      * If the zone is a {@code ZoneOffset} or it cannot be obtained then
   759      * If the zone is a {@code ZoneOffset} or it cannot be obtained then
   727      * an exception is thrown unless the section of the formatter is optional.
   760      * an exception is thrown unless the section of the formatter is optional.
   728      * If the zone is not an offset, then the zone will be printed using
   761      * If the zone is not an offset, then the zone will be printed using
   729      * the zone ID from {@link ZoneId#getId()}.
   762      * the zone ID from {@link ZoneId#getId()}.
   730      * <p>
   763      * <p>
   731      * During parsing, the zone is parsed and must match a known zone or offset.
   764      * During parsing, the text must match a known zone or offset.
   732      * If the zone cannot be parsed then an exception is thrown unless the
   765      * There are two types of zone ID, offset-based, such as '+01:30' and
   733      * section of the formatter is optional.
   766      * region-based, such as 'Europe/London'. These are parsed differently.
   734      * Note that parsing accepts offsets, whereas printing will never produce
   767      * If the parse starts with '+', '-', 'UT', 'UTC' or 'GMT', then the parser
   735      * one, thus parsing is equivalent to {@code appendZoneId}.
   768      * expects an offset-based zone and will not match region-based zones.
       
   769      * The offset ID, such as '+02:30', may be at the start of the parse,
       
   770      * or prefixed by  'UT', 'UTC' or 'GMT'. The offset ID parsing is
       
   771      * equivalent to using {@link #appendOffset(String, String)} using the
       
   772      * arguments 'HH:MM:ss' and the no offset string '0'.
       
   773      * If the parse starts with 'UT', 'UTC' or 'GMT', and the parser cannot
       
   774      * match a following offset ID, then {@link ZoneOffset#UTC} is selected.
       
   775      * In all other cases, the list of known region-based zones is used to
       
   776      * find the longest available match. If no match is found, and the parse
       
   777      * starts with 'Z', then {@code ZoneOffset.UTC} is selected.
       
   778      * The parser uses the {@linkplain #parseCaseInsensitive() case sensitive} setting.
       
   779      * <p>
       
   780      * For example, the following will parse:
       
   781      * <pre>
       
   782      *   "Europe/London"           -> ZoneId.of("Europe/London")
       
   783      *   "Z"                       -> ZoneOffset.UTC
       
   784      *   "UT"                      -> ZoneOffset.UTC
       
   785      *   "UTC"                     -> ZoneOffset.UTC
       
   786      *   "GMT"                     -> ZoneOffset.UTC
       
   787      *   "UT0"                     -> ZoneOffset.UTC
       
   788      *   "UTC0"                    -> ZoneOffset.UTC
       
   789      *   "GMT0"                    -> ZoneOffset.UTC
       
   790      *   "+01:30"                  -> ZoneOffset.of("+01:30")
       
   791      *   "UT+01:30"                -> ZoneOffset.of("+01:30")
       
   792      *   "UTC+01:30"               -> ZoneOffset.of("+01:30")
       
   793      *   "GMT+01:30"               -> ZoneOffset.of("+01:30")
       
   794      * </pre>
       
   795      * <p>
       
   796      * Note that this method is is identical to {@code appendZoneId()} except
       
   797      * in the mechanism used to obtain the zone.
       
   798      * Note also that parsing accepts offsets, whereas formatting will never
       
   799      * produce one.
   736      *
   800      *
   737      * @return this, for chaining, not null
   801      * @return this, for chaining, not null
   738      * @see #appendZoneId()
   802      * @see #appendZoneId()
   739      */
   803      */
   740     public DateTimeFormatterBuilder appendZoneRegionId() {
   804     public DateTimeFormatterBuilder appendZoneRegionId() {
   744 
   808 
   745     /**
   809     /**
   746      * Appends the time-zone ID, such as 'Europe/Paris' or '+02:00', to
   810      * Appends the time-zone ID, such as 'Europe/Paris' or '+02:00', to
   747      * the formatter, using the best available zone ID.
   811      * the formatter, using the best available zone ID.
   748      * <p>
   812      * <p>
   749      * This appends an instruction to print/parse the best available
   813      * This appends an instruction to format/parse the best available
   750      * zone or offset ID to the builder.
   814      * zone or offset ID to the builder.
   751      * The zone ID is obtained in a lenient manner that first attempts to
   815      * The zone ID is obtained in a lenient manner that first attempts to
   752      * find a true zone ID, such as that on {@code ZonedDateTime}, and
   816      * find a true zone ID, such as that on {@code ZonedDateTime}, and
   753      * then attempts to find an offset, such as that on {@code OffsetDateTime}.
   817      * then attempts to find an offset, such as that on {@code OffsetDateTime}.
   754      * <p>
   818      * <p>
   755      * During printing, the zone is obtained using a mechanism equivalent
   819      * During formatting, the zone is obtained using a mechanism equivalent
   756      * to querying the temporal with {@link Queries#zone()}.
   820      * to querying the temporal with {@link Queries#zone()}.
   757      * It will be printed using the result of {@link ZoneId#getId()}.
   821      * It will be printed using the result of {@link ZoneId#getId()}.
   758      * If the zone cannot be obtained then an exception is thrown unless the
   822      * If the zone cannot be obtained then an exception is thrown unless the
   759      * section of the formatter is optional.
   823      * section of the formatter is optional.
   760      * <p>
   824      * <p>
   761      * During parsing, the zone is parsed and must match a known zone or offset.
   825      * During parsing, the text must match a known zone or offset.
   762      * If the zone cannot be parsed then an exception is thrown unless the
   826      * There are two types of zone ID, offset-based, such as '+01:30' and
   763      * section of the formatter is optional.
   827      * region-based, such as 'Europe/London'. These are parsed differently.
   764      * <p>
   828      * If the parse starts with '+', '-', 'UT', 'UTC' or 'GMT', then the parser
   765      * This method is is identical to {@code appendZoneId()} except in the
   829      * expects an offset-based zone and will not match region-based zones.
   766      * mechanism used to obtain the zone.
   830      * The offset ID, such as '+02:30', may be at the start of the parse,
       
   831      * or prefixed by  'UT', 'UTC' or 'GMT'. The offset ID parsing is
       
   832      * equivalent to using {@link #appendOffset(String, String)} using the
       
   833      * arguments 'HH:MM:ss' and the no offset string '0'.
       
   834      * If the parse starts with 'UT', 'UTC' or 'GMT', and the parser cannot
       
   835      * match a following offset ID, then {@link ZoneOffset#UTC} is selected.
       
   836      * In all other cases, the list of known region-based zones is used to
       
   837      * find the longest available match. If no match is found, and the parse
       
   838      * starts with 'Z', then {@code ZoneOffset.UTC} is selected.
       
   839      * The parser uses the {@linkplain #parseCaseInsensitive() case sensitive} setting.
       
   840      * <p>
       
   841      * For example, the following will parse:
       
   842      * <pre>
       
   843      *   "Europe/London"           -> ZoneId.of("Europe/London")
       
   844      *   "Z"                       -> ZoneOffset.UTC
       
   845      *   "UT"                      -> ZoneOffset.UTC
       
   846      *   "UTC"                     -> ZoneOffset.UTC
       
   847      *   "GMT"                     -> ZoneOffset.UTC
       
   848      *   "UT0"                     -> ZoneOffset.UTC
       
   849      *   "UTC0"                    -> ZoneOffset.UTC
       
   850      *   "GMT0"                    -> ZoneOffset.UTC
       
   851      *   "+01:30"                  -> ZoneOffset.of("+01:30")
       
   852      *   "UT+01:30"                -> ZoneOffset.of("+01:30")
       
   853      *   "UTC+01:30"               -> ZoneOffset.of("+01:30")
       
   854      *   "GMT+01:30"               -> ZoneOffset.of("+01:30")
       
   855      * </pre>
       
   856      * <p>
       
   857      * Note that this method is is identical to {@code appendZoneId()} except
       
   858      * in the mechanism used to obtain the zone.
   767      *
   859      *
   768      * @return this, for chaining, not null
   860      * @return this, for chaining, not null
   769      * @see #appendZoneId()
   861      * @see #appendZoneId()
   770      */
   862      */
   771     public DateTimeFormatterBuilder appendZoneOrOffsetId() {
   863     public DateTimeFormatterBuilder appendZoneOrOffsetId() {
   774     }
   866     }
   775 
   867 
   776     /**
   868     /**
   777      * Appends the time-zone name, such as 'British Summer Time', to the formatter.
   869      * Appends the time-zone name, such as 'British Summer Time', to the formatter.
   778      * <p>
   870      * <p>
   779      * This appends an instruction to print the textual name of the zone to the builder.
   871      * This appends an instruction to format/parse the textual name of the zone to
   780      * <p>
   872      * the builder.
   781      * During printing, the zone is obtained using a mechanism equivalent
   873      * <p>
       
   874      * During formatting, the zone is obtained using a mechanism equivalent
   782      * to querying the temporal with {@link Queries#zoneId()}.
   875      * to querying the temporal with {@link Queries#zoneId()}.
   783      * If the zone is a {@code ZoneOffset} it will be printed using the
   876      * If the zone is a {@code ZoneOffset} it will be printed using the
   784      * result of {@link ZoneOffset#getId()}.
   877      * result of {@link ZoneOffset#getId()}.
   785      * If the zone is not an offset, the textual name will be looked up
   878      * If the zone is not an offset, the textual name will be looked up
   786      * for the locale set in the {@link DateTimeFormatter}.
   879      * for the locale set in the {@link DateTimeFormatter}.
   789      * If the lookup for text does not find any suitable reuslt, then the
   882      * If the lookup for text does not find any suitable reuslt, then the
   790      * {@link ZoneId#getId() ID} will be printed instead.
   883      * {@link ZoneId#getId() ID} will be printed instead.
   791      * If the zone cannot be obtained then an exception is thrown unless the
   884      * If the zone cannot be obtained then an exception is thrown unless the
   792      * section of the formatter is optional.
   885      * section of the formatter is optional.
   793      * <p>
   886      * <p>
   794      * Parsing is not currently supported.
   887      * During parsing, either the textual zone name, the zone ID or the offset
       
   888      * is accepted. Many textual zone names are not unique, such as CST can be
       
   889      * for both "Central Standard Time" and "China Standard Time". In this
       
   890      * situation, the zone id will be determined by the region information from
       
   891      * formatter's  {@link DateTimeFormatter#getLocale() locale} and the standard
       
   892      * zone id for that area, for example, America/New_York for the America Eastern
       
   893      * zone. The {@link #appendZoneText(TextStyle, Set)} may be used
       
   894      * to specify a set of preferred {@link ZoneId} in this situation.
   795      *
   895      *
   796      * @param textStyle  the text style to use, not null
   896      * @param textStyle  the text style to use, not null
   797      * @return this, for chaining, not null
   897      * @return this, for chaining, not null
   798      */
   898      */
   799     public DateTimeFormatterBuilder appendZoneText(TextStyle textStyle) {
   899     public DateTimeFormatterBuilder appendZoneText(TextStyle textStyle) {
   800         // TODO: parsing of zone text?
   900         appendInternal(new ZoneTextPrinterParser(textStyle, null));
   801 //        * During parsing, either the textual zone name, the zone ID or the offset
       
   802 //        * is accepted.
       
   803 //        * If the zone cannot be parsed then an exception is thrown unless the
       
   804 //        * section of the formatter is optional.
       
   805         appendInternal(new ZoneTextPrinterParser(textStyle));
       
   806         return this;
   901         return this;
   807     }
   902     }
   808 
   903 
       
   904     /**
       
   905      * Appends the time-zone name, such as 'British Summer Time', to the formatter.
       
   906      * <p>
       
   907      * This appends an instruction to format/parse the textual name of the zone to
       
   908      * the builder.
       
   909      * <p>
       
   910      * During formatting, the zone is obtained using a mechanism equivalent
       
   911      * to querying the temporal with {@link Queries#zoneId()}.
       
   912      * If the zone is a {@code ZoneOffset} it will be printed using the
       
   913      * result of {@link ZoneOffset#getId()}.
       
   914      * If the zone is not an offset, the textual name will be looked up
       
   915      * for the locale set in the {@link DateTimeFormatter}.
       
   916      * If the temporal object being printed represents an instant, then the text
       
   917      * will be the summer or winter time text as appropriate.
       
   918      * If the lookup for text does not find any suitable reuslt, then the
       
   919      * {@link ZoneId#getId() ID} will be printed instead.
       
   920      * If the zone cannot be obtained then an exception is thrown unless the
       
   921      * section of the formatter is optional.
       
   922      * <p>
       
   923      * During parsing, either the textual zone name, the zone ID or the offset
       
   924      * is accepted. Many textual zone names are not unique, such as CST can be
       
   925      * for both "Central Standard Time" and "China Standard Time". In this
       
   926      * situation, the zone id will be determined by the region information from
       
   927      * formatter's  {@link DateTimeFormatter#getLocale() locale} and the standard
       
   928      * zone id for that area, for example, America/New_York for the America Eastern
       
   929      * zone. This method also allows a set of preferred {@link ZoneId} to be
       
   930      * specified for parsing. The matched preferred zone id will be used if the
       
   931      * textural zone name being parsed is not unique.
       
   932      *
       
   933      * If the zone cannot be parsed then an exception is thrown unless the
       
   934      * section of the formatter is optional.
       
   935      *
       
   936      * @param textStyle  the text style to use, not null
       
   937      * @param preferredZones  the set of preferred zone ids, not null
       
   938      * @return this, for chaining, not null
       
   939      */
       
   940     public DateTimeFormatterBuilder appendZoneText(TextStyle textStyle,
       
   941                                                    Set<ZoneId> preferredZones) {
       
   942         Objects.requireNonNull(preferredZones, "preferredZones");
       
   943         appendInternal(new ZoneTextPrinterParser(textStyle, preferredZones));
       
   944         return this;
       
   945     }
       
   946 
   809     //-----------------------------------------------------------------------
   947     //-----------------------------------------------------------------------
   810     /**
   948     /**
   811      * Appends the chronology ID to the formatter.
   949      * Appends the chronology ID, such as 'ISO' or 'ThaiBuddhist', to the formatter.
   812      * <p>
   950      * <p>
   813      * The chronology ID will be output during a print.
   951      * This appends an instruction to format/parse the chronology ID to the builder.
   814      * If the chronology cannot be obtained then an exception will be thrown.
   952      * <p>
       
   953      * During formatting, the chronology is obtained using a mechanism equivalent
       
   954      * to querying the temporal with {@link Queries#chronology()}.
       
   955      * It will be printed using the result of {@link Chronology#getId()}.
       
   956      * If the chronology cannot be obtained then an exception is thrown unless the
       
   957      * section of the formatter is optional.
       
   958      * <p>
       
   959      * During parsing, the chronology is parsed and must match one of the chronologies
       
   960      * in {@link Chronology#getAvailableChronologies()}.
       
   961      * If the chronology cannot be parsed then an exception is thrown unless the
       
   962      * section of the formatter is optional.
       
   963      * The parser uses the {@linkplain #parseCaseInsensitive() case sensitive} setting.
   815      *
   964      *
   816      * @return this, for chaining, not null
   965      * @return this, for chaining, not null
   817      */
   966      */
   818     public DateTimeFormatterBuilder appendChronoId() {
   967     public DateTimeFormatterBuilder appendChronologyId() {
   819         appendInternal(new ChronoPrinterParser(null));
   968         appendInternal(new ChronoPrinterParser(null));
   820         return this;
   969         return this;
   821     }
   970     }
   822 
   971 
   823     /**
   972     /**
   824      * Appends the chronology name to the formatter.
   973      * Appends the chronology name to the formatter.
   825      * <p>
   974      * <p>
   826      * The calendar system name will be output during a print.
   975      * The calendar system name will be output during a format.
   827      * If the chronology cannot be obtained then an exception will be thrown.
   976      * If the chronology cannot be obtained then an exception will be thrown.
   828      * The calendar system name is obtained from the formatting symbols.
   977      * The calendar system name is obtained from the formatting symbols.
   829      *
   978      *
   830      * @param textStyle  the text style to use, not null
   979      * @param textStyle  the text style to use, not null
   831      * @return this, for chaining, not null
   980      * @return this, for chaining, not null
   832      */
   981      */
   833     public DateTimeFormatterBuilder appendChronoText(TextStyle textStyle) {
   982     public DateTimeFormatterBuilder appendChronologyText(TextStyle textStyle) {
   834         Objects.requireNonNull(textStyle, "textStyle");
   983         Objects.requireNonNull(textStyle, "textStyle");
   835         appendInternal(new ChronoPrinterParser(textStyle));
   984         appendInternal(new ChronoPrinterParser(textStyle));
   836         return this;
   985         return this;
   837     }
   986     }
   838 
   987 
   839     //-----------------------------------------------------------------------
   988     //-----------------------------------------------------------------------
   840     /**
   989     /**
   841      * Appends a localized date-time pattern to the formatter.
   990      * Appends a localized date-time pattern to the formatter.
   842      * <p>
   991      * <p>
   843      * The pattern is resolved lazily using the locale being used during the print/parse
   992      * This appends a localized section to the builder, suitable for outputting
   844      * (stored in {@link DateTimeFormatter}.
   993      * a date, time or date-time combination. The format of the localized
   845      * <p>
   994      * section is lazily looked up based on four items:
   846      * The pattern can vary by chronology, although typically it doesn't.
   995      * <p><ul>
   847      * This method uses the standard ISO chronology patterns.
   996      * <li>the {@code dateStyle} specified to this method
       
   997      * <li>the {@code timeStyle} specified to this method
       
   998      * <li>the {@code Locale} of the {@code DateTimeFormatter}
       
   999      * <li>the {@code Chronology}, selecting the best available
       
  1000      * </ul><p>
       
  1001      * During formatting, the chronology is obtained from the temporal object
       
  1002      * being formatted, which may have been overridden by
       
  1003      * {@link DateTimeFormatter#withChronology(Chronology)}.
       
  1004      * <p>
       
  1005      * During parsing, if a chronology has already been parsed, then it is used.
       
  1006      * Otherwise the default from {@code DateTimeFormatter.withChronology(Chronology)}
       
  1007      * is used, with {@code IsoChronology} as the fallback.
       
  1008      * <p>
       
  1009      * Note that this method provides similar functionality to methods on
       
  1010      * {@code DateFormat} such as {@link DateFormat#getDateTimeInstance(int, int)}.
   848      *
  1011      *
   849      * @param dateStyle  the date style to use, null means no date required
  1012      * @param dateStyle  the date style to use, null means no date required
   850      * @param timeStyle  the time style to use, null means no time required
  1013      * @param timeStyle  the time style to use, null means no time required
   851      * @return this, for chaining, not null
  1014      * @return this, for chaining, not null
       
  1015      * @throws IllegalArgumentException if both the date and time styles are null
   852      */
  1016      */
   853     public DateTimeFormatterBuilder appendLocalized(FormatStyle dateStyle, FormatStyle timeStyle) {
  1017     public DateTimeFormatterBuilder appendLocalized(FormatStyle dateStyle, FormatStyle timeStyle) {
   854         return appendLocalized(dateStyle, timeStyle, ISOChrono.INSTANCE);
  1018         if (dateStyle == null && timeStyle == null) {
   855     }
  1019             throw new IllegalArgumentException("Either the date or time style must be non-null");
   856 
  1020         }
   857     /**
  1021         appendInternal(new LocalizedPrinterParser(dateStyle, timeStyle));
   858      * Appends a localized date-time pattern to the formatter.
       
   859      * <p>
       
   860      * The pattern is resolved lazily using the locale being used during the print/parse,
       
   861      * stored in {@link DateTimeFormatter}.
       
   862      * <p>
       
   863      * The pattern can vary by chronology, although typically it doesn't.
       
   864      * This method allows the chronology to be specified.
       
   865      *
       
   866      * @param dateStyle  the date style to use, null means no date required
       
   867      * @param timeStyle  the time style to use, null means no time required
       
   868      * @param chrono  the chronology to use, not null
       
   869      * @return this, for chaining, not null
       
   870      */
       
   871     public DateTimeFormatterBuilder appendLocalized(FormatStyle dateStyle, FormatStyle timeStyle, Chrono<?> chrono) {
       
   872         Objects.requireNonNull(chrono, "chrono");
       
   873         if (dateStyle != null || timeStyle != null) {
       
   874             appendInternal(new LocalizedPrinterParser(dateStyle, timeStyle, chrono));
       
   875         }
       
   876         return this;
  1022         return this;
   877     }
  1023     }
   878 
  1024 
   879     //-----------------------------------------------------------------------
  1025     //-----------------------------------------------------------------------
   880     /**
  1026     /**
   881      * Appends a character literal to the formatter.
  1027      * Appends a character literal to the formatter.
   882      * <p>
  1028      * <p>
   883      * This character will be output during a print.
  1029      * This character will be output during a format.
   884      *
  1030      *
   885      * @param literal  the literal to append, not null
  1031      * @param literal  the literal to append, not null
   886      * @return this, for chaining, not null
  1032      * @return this, for chaining, not null
   887      */
  1033      */
   888     public DateTimeFormatterBuilder appendLiteral(char literal) {
  1034     public DateTimeFormatterBuilder appendLiteral(char literal) {
   891     }
  1037     }
   892 
  1038 
   893     /**
  1039     /**
   894      * Appends a string literal to the formatter.
  1040      * Appends a string literal to the formatter.
   895      * <p>
  1041      * <p>
   896      * This string will be output during a print.
  1042      * This string will be output during a format.
   897      * <p>
  1043      * <p>
   898      * If the literal is empty, nothing is added to the formatter.
  1044      * If the literal is empty, nothing is added to the formatter.
   899      *
  1045      *
   900      * @param literal  the literal to append, not null
  1046      * @param literal  the literal to append, not null
   901      * @return this, for chaining, not null
  1047      * @return this, for chaining, not null
   927         appendInternal(formatter.toPrinterParser(false));
  1073         appendInternal(formatter.toPrinterParser(false));
   928         return this;
  1074         return this;
   929     }
  1075     }
   930 
  1076 
   931     /**
  1077     /**
   932      * Appends a formatter to the builder which will optionally print/parse.
  1078      * Appends a formatter to the builder which will optionally format/parse.
   933      * <p>
  1079      * <p>
   934      * This method has the same effect as appending each of the constituent
  1080      * This method has the same effect as appending each of the constituent
   935      * parts directly to this builder surrounded by an {@link #optionalStart()} and
  1081      * parts directly to this builder surrounded by an {@link #optionalStart()} and
   936      * {@link #optionalEnd()}.
  1082      * {@link #optionalEnd()}.
   937      * <p>
  1083      * <p>
   938      * The formatter will print if data is available for all the fields contained within it.
  1084      * The formatter will format if data is available for all the fields contained within it.
   939      * The formatter will parse if the string matches, otherwise no error is returned.
  1085      * The formatter will parse if the string matches, otherwise no error is returned.
   940      *
  1086      *
   941      * @param formatter  the formatter to add, not null
  1087      * @param formatter  the formatter to add, not null
   942      * @return this, for chaining, not null
  1088      * @return this, for chaining, not null
   943      */
  1089      */
   956      * The characters '[' and ']' indicate optional patterns.
  1102      * The characters '[' and ']' indicate optional patterns.
   957      * The following pattern letters are defined:
  1103      * The following pattern letters are defined:
   958      * <pre>
  1104      * <pre>
   959      *  Symbol  Meaning                     Presentation      Examples
  1105      *  Symbol  Meaning                     Presentation      Examples
   960      *  ------  -------                     ------------      -------
  1106      *  ------  -------                     ------------      -------
   961      *   G       era                         number/text       1; 01; AD; Anno Domini
  1107      *   G       era                         text              A; AD; Anno Domini
   962      *   y       year                        year              2004; 04
  1108      *   y       year                        year              2004; 04
   963      *   D       day-of-year                 number            189
  1109      *   D       day-of-year                 number            189
   964      *   M       month-of-year               number/text       7; 07; Jul; July; J
  1110      *   M       month-of-year               number/text       7; 07; Jul; July; J
   965      *   d       day-of-month                number            10
  1111      *   d       day-of-month                number            10
   966      *
  1112      *
   983      *   S       fraction-of-second          fraction          978
  1129      *   S       fraction-of-second          fraction          978
   984      *   A       milli-of-day                number            1234
  1130      *   A       milli-of-day                number            1234
   985      *   n       nano-of-second              number            987654321
  1131      *   n       nano-of-second              number            987654321
   986      *   N       nano-of-day                 number            1234000000
  1132      *   N       nano-of-day                 number            1234000000
   987      *
  1133      *
   988      *   I       time-zone ID                zoneId            America/Los_Angeles
  1134      *   V       time-zone ID                zone-id           America/Los_Angeles; Z; -08:30
   989      *   z       time-zone name              text              Pacific Standard Time; PST
  1135      *   z       time-zone name              zone-name         Pacific Standard Time; PST
       
  1136      *   X       zone-offset 'Z' for zero    offset-X          Z; -08; -0830; -08:30; -083015; -08:30:15;
       
  1137      *   x       zone-offset                 offset-x          +0000; -08; -0830; -08:30; -083015; -08:30:15;
   990      *   Z       zone-offset                 offset-Z          +0000; -0800; -08:00;
  1138      *   Z       zone-offset                 offset-Z          +0000; -0800; -08:00;
   991      *   X       zone-offset 'Z' for zero    offset-X          Z; -08; -0830; -08:30; -083015; -08:30:15;
       
   992      *
  1139      *
   993      *   p       pad next                    pad modifier      1
  1140      *   p       pad next                    pad modifier      1
   994      *
  1141      *
   995      *   '       escape for text             delimiter
  1142      *   '       escape for text             delimiter
   996      *   ''      single quote                literal           '
  1143      *   ''      single quote                literal           '
  1021      * When parsing in lenient mode, the number of parsed digits must be at least the count of pattern
  1168      * When parsing in lenient mode, the number of parsed digits must be at least the count of pattern
  1022      * letters, up to 9 digits.
  1169      * letters, up to 9 digits.
  1023      * <p>
  1170      * <p>
  1024      * <b>Year</b>: The count of letters determines the minimum field width below which padding is used.
  1171      * <b>Year</b>: The count of letters determines the minimum field width below which padding is used.
  1025      * If the count of letters is two, then a {@link #appendValueReduced reduced} two digit form is used.
  1172      * If the count of letters is two, then a {@link #appendValueReduced reduced} two digit form is used.
  1026      * For printing, this outputs the rightmost two digits. For parsing, this will parse using the
  1173      * For formatting, this outputs the rightmost two digits. For parsing, this will parse using the
  1027      * base value of 2000, resulting in a year within the range 2000 to 2099 inclusive.
  1174      * base value of 2000, resulting in a year within the range 2000 to 2099 inclusive.
  1028      * If the count of letters is less than four (but not two), then the sign is only output for negative
  1175      * If the count of letters is less than four (but not two), then the sign is only output for negative
  1029      * years as per {@link SignStyle#NORMAL}.
  1176      * years as per {@link SignStyle#NORMAL}.
  1030      * Otherwise, the sign is output if the pad width is exceeded, as per {@link SignStyle#EXCEEDS_PAD}
  1177      * Otherwise, the sign is output if the pad width is exceeded, as per {@link SignStyle#EXCEEDS_PAD}
  1031      * <p>
  1178      * <p>
  1032      * <b>ZoneId</b>: 'I' outputs the zone ID, such as 'Europe/Paris'.
  1179      * <b>ZoneId</b>: This outputs the time-zone ID, such as 'Europe/Paris'.
  1033      * <p>
  1180      * If the count of letters is two, then the time-zone ID is output.
  1034      * <b>Offset X</b>: This formats the offset using 'Z' when the offset is zero.
  1181      * Any other count of letters throws {@code IllegalArgumentException}.
  1035      * One letter outputs just the hour', such as '+01'
  1182      * <pre>
       
  1183      *  Pattern     Equivalent builder methods
       
  1184      *   VV          appendZoneId()
       
  1185      * </pre>
       
  1186      * <p>
       
  1187      * <b>Zone names</b>: This outputs the display name of the time-zone ID.
       
  1188      * If the count of letters is one, two or three, then the short name is output.
       
  1189      * If the count of letters is four, then the full name is output.
       
  1190      * Five or more letters throws {@code IllegalArgumentException}.
       
  1191      * <pre>
       
  1192      *  Pattern     Equivalent builder methods
       
  1193      *   z           appendZoneText(TextStyle.SHORT)
       
  1194      *   zz          appendZoneText(TextStyle.SHORT)
       
  1195      *   zzz         appendZoneText(TextStyle.SHORT)
       
  1196      *   zzzz        appendZoneText(TextStyle.FULL)
       
  1197      * </pre>
       
  1198      * <p>
       
  1199      * <b>Offset X and x</b>: This formats the offset based on the number of pattern letters.
       
  1200      * One letter outputs just the hour', such as '+01', unless the minute is non-zero
       
  1201      * in which case the minute is also output, such as '+0130'.
  1036      * Two letters outputs the hour and minute, without a colon, such as '+0130'.
  1202      * Two letters outputs the hour and minute, without a colon, such as '+0130'.
  1037      * Three letters outputs the hour and minute, with a colon, such as '+01:30'.
  1203      * Three letters outputs the hour and minute, with a colon, such as '+01:30'.
  1038      * Four letters outputs the hour and minute and optional second, without a colon, such as '+013015'.
  1204      * Four letters outputs the hour and minute and optional second, without a colon, such as '+013015'.
  1039      * Five letters outputs the hour and minute and optional second, with a colon, such as '+01:30:15'.
  1205      * Five letters outputs the hour and minute and optional second, with a colon, such as '+01:30:15'.
  1040      * <p>
  1206      * Six or more letters throws {@code IllegalArgumentException}.
  1041      * <b>Offset Z</b>: This formats the offset using '+0000' or '+00:00' when the offset is zero.
  1207      * Pattern letter 'X' (upper case) will output 'Z' when the offset to be output would be zero,
  1042      * One or two letters outputs the hour and minute, without a colon, such as '+0130'.
  1208      * whereas pattern letter 'x' (lower case) will output '+00', '+0000', or '+00:00'.
  1043      * Three letters outputs the hour and minute, with a colon, such as '+01:30'.
  1209      * <pre>
  1044      * <p>
  1210      *  Pattern     Equivalent builder methods
  1045      * <b>Zone names</b>: Time zone names ('z') cannot be parsed.
  1211      *   X           appendOffset("+HHmm","Z")
       
  1212      *   XX          appendOffset("+HHMM","Z")
       
  1213      *   XXX         appendOffset("+HH:MM","Z")
       
  1214      *   XXXX        appendOffset("+HHMMss","Z")
       
  1215      *   XXXXX       appendOffset("+HH:MM:ss","Z")
       
  1216      *   x           appendOffset("+HHmm","+00")
       
  1217      *   xx          appendOffset("+HHMM","+0000")
       
  1218      *   xxx         appendOffset("+HH:MM","+00:00")
       
  1219      *   xxxx        appendOffset("+HHMMss","+0000")
       
  1220      *   xxxxx       appendOffset("+HH:MM:ss","+00:00")
       
  1221      * </pre>
       
  1222      * <p>
       
  1223      * <b>Offset Z</b>: This formats the offset based on the number of pattern letters.
       
  1224      * One, two or three letters outputs the hour and minute, without a colon, such as '+0130'.
       
  1225      * Four or more letters throws {@code IllegalArgumentException}.
       
  1226      * The output will be '+0000' when the offset is zero.
       
  1227      * <pre>
       
  1228      *  Pattern     Equivalent builder methods
       
  1229      *   Z           appendOffset("+HHMM","+0000")
       
  1230      *   ZZ          appendOffset("+HHMM","+0000")
       
  1231      *   ZZZ         appendOffset("+HHMM","+0000")
       
  1232      * </pre>
  1046      * <p>
  1233      * <p>
  1047      * <b>Optional section</b>: The optional section markers work exactly like calling {@link #optionalStart()}
  1234      * <b>Optional section</b>: The optional section markers work exactly like calling {@link #optionalStart()}
  1048      * and {@link #optionalEnd()}.
  1235      * and {@link #optionalEnd()}.
  1049      * <p>
  1236      * <p>
  1050      * <b>Pad modifier</b>: Modifies the pattern that immediately follows to be padded with spaces.
  1237      * <b>Pad modifier</b>: Modifies the pattern that immediately follows to be padded with spaces.
  1056      * Any unrecognized letter is an error.
  1243      * Any unrecognized letter is an error.
  1057      * Any non-letter character, other than '[', ']', '{', '}' and the single quote will be output directly.
  1244      * Any non-letter character, other than '[', ']', '{', '}' and the single quote will be output directly.
  1058      * Despite this, it is recommended to use single quotes around all characters that you want to
  1245      * Despite this, it is recommended to use single quotes around all characters that you want to
  1059      * output directly to ensure that future changes do not break your application.
  1246      * output directly to ensure that future changes do not break your application.
  1060      * <p>
  1247      * <p>
  1061      * The pattern string is similar, but not identical, to {@link java.text.SimpleDateFormat SimpleDateFormat}.
  1248      * Note that the pattern string is similar, but not identical, to
       
  1249      * {@link java.text.SimpleDateFormat SimpleDateFormat}.
       
  1250      * The pattern string is also similar, but not identical, to that defined by the
       
  1251      * Unicode Common Locale Data Repository (CLDR/LDML).
  1062      * Pattern letters 'E' and 'u' are merged, which changes the meaning of "E" and "EE" to be numeric.
  1252      * Pattern letters 'E' and 'u' are merged, which changes the meaning of "E" and "EE" to be numeric.
  1063      * Pattern letters 'Z' and 'X' are extended.
  1253      * Pattern letters 'X' is aligned with Unicode CLDR/LDML, which affects pattern 'X'.
  1064      * Pattern letter 'y' and 'Y' parse years of two digits and more than 4 digits differently.
  1254      * Pattern letter 'y' and 'Y' parse years of two digits and more than 4 digits differently.
  1065      * Pattern letters 'n', 'A', 'N', 'I' and 'p' are added.
  1255      * Pattern letters 'n', 'A', 'N', 'I' and 'p' are added.
  1066      * Number types will reject large numbers.
  1256      * Number types will reject large numbers.
  1067      * The pattern string is also similar, but not identical, to that defined by the
       
  1068      * Unicode Common Locale Data Repository (CLDR).
       
  1069      *
  1257      *
  1070      * @param pattern  the pattern to add, not null
  1258      * @param pattern  the pattern to add, not null
  1071      * @return this, for chaining, not null
  1259      * @return this, for chaining, not null
  1072      * @throws IllegalArgumentException if the pattern is invalid
  1260      * @throws IllegalArgumentException if the pattern is invalid
  1073      */
  1261      */
  1105                 // main rules
  1293                 // main rules
  1106                 TemporalField field = FIELD_MAP.get(cur);
  1294                 TemporalField field = FIELD_MAP.get(cur);
  1107                 if (field != null) {
  1295                 if (field != null) {
  1108                     parseField(cur, count, field);
  1296                     parseField(cur, count, field);
  1109                 } else if (cur == 'z') {
  1297                 } else if (cur == 'z') {
  1110                     if (count < 4) {
  1298                     if (count > 4) {
       
  1299                         throw new IllegalArgumentException("Too many pattern letters: " + cur);
       
  1300                     } else if (count == 4) {
       
  1301                         appendZoneText(TextStyle.FULL);
       
  1302                     } else {
  1111                         appendZoneText(TextStyle.SHORT);
  1303                         appendZoneText(TextStyle.SHORT);
  1112                     } else {
  1304                     }
  1113                         appendZoneText(TextStyle.FULL);
  1305                 } else if (cur == 'V') {
  1114                     }
  1306                     if (count != 2) {
  1115                 } else if (cur == 'I') {
  1307                         throw new IllegalArgumentException("Pattern letter count must be 2: " + cur);
       
  1308                     }
  1116                     appendZoneId();
  1309                     appendZoneId();
  1117                 } else if (cur == 'Z') {
  1310                 } else if (cur == 'Z') {
  1118                     if (count > 3) {
  1311                     if (count > 3) {
  1119                         throw new IllegalArgumentException("Too many pattern letters: " + cur);
  1312                         throw new IllegalArgumentException("Too many pattern letters: " + cur);
  1120                     }
  1313                     }
  1121                     if (count < 3) {
  1314                     appendOffset("+HHMM", "+0000");
  1122                         appendOffset("+HHMM", "+0000");
       
  1123                     } else {
       
  1124                         appendOffset("+HH:MM", "+00:00");
       
  1125                     }
       
  1126                 } else if (cur == 'X') {
  1315                 } else if (cur == 'X') {
  1127                     if (count > 5) {
  1316                     if (count > 5) {
  1128                         throw new IllegalArgumentException("Too many pattern letters: " + cur);
  1317                         throw new IllegalArgumentException("Too many pattern letters: " + cur);
  1129                     }
  1318                     }
  1130                     appendOffset(OffsetIdPrinterParser.PATTERNS[count - 1], "Z");
  1319                     appendOffset(OffsetIdPrinterParser.PATTERNS[count + (count == 1 ? 0 : 1)], "Z");
       
  1320                 } else if (cur == 'x') {
       
  1321                     if (count > 5) {
       
  1322                         throw new IllegalArgumentException("Too many pattern letters: " + cur);
       
  1323                     }
       
  1324                     String zero = (count == 1 ? "+00" : (count % 2 == 0 ? "+0000" : "+00:00"));
       
  1325                     appendOffset(OffsetIdPrinterParser.PATTERNS[count + (count == 1 ? 0 : 1)], zero);
  1131                 } else if (cur == 'w' || cur == 'e') {
  1326                 } else if (cur == 'w' || cur == 'e') {
  1132                     // Fields defined by Locale
  1327                     // Fields defined by Locale
  1133                     if (count > 1) {
  1328                     if (count > 1) {
  1134                         throw new IllegalArgumentException("Too many pattern letters: " + cur);
  1329                         throw new IllegalArgumentException("Too many pattern letters: " + cur);
  1135                     }
  1330                     }
  1194                     appendValue(field, count, 19, SignStyle.NORMAL);
  1389                     appendValue(field, count, 19, SignStyle.NORMAL);
  1195                 } else {
  1390                 } else {
  1196                     appendValue(field, count, 19, SignStyle.EXCEEDS_PAD);
  1391                     appendValue(field, count, 19, SignStyle.EXCEEDS_PAD);
  1197                 }
  1392                 }
  1198                 break;
  1393                 break;
  1199             case 'G':
       
  1200             case 'M':
  1394             case 'M':
  1201             case 'Q':
  1395             case 'Q':
  1202             case 'E':
  1396             case 'E':
  1203                 switch (count) {
  1397                 switch (count) {
  1204                     case 1:
  1398                     case 1:
  1218                         break;
  1412                         break;
  1219                     default:
  1413                     default:
  1220                         throw new IllegalArgumentException("Too many pattern letters: " + cur);
  1414                         throw new IllegalArgumentException("Too many pattern letters: " + cur);
  1221                 }
  1415                 }
  1222                 break;
  1416                 break;
       
  1417             case 'G':
  1223             case 'a':
  1418             case 'a':
  1224                 switch (count) {
  1419                 switch (count) {
  1225                     case 1:
  1420                     case 1:
  1226                     case 2:
  1421                     case 2:
  1227                     case 3:
  1422                     case 3:
  1251     }
  1446     }
  1252 
  1447 
  1253     /** Map of letters to fields. */
  1448     /** Map of letters to fields. */
  1254     private static final Map<Character, TemporalField> FIELD_MAP = new HashMap<>();
  1449     private static final Map<Character, TemporalField> FIELD_MAP = new HashMap<>();
  1255     static {
  1450     static {
  1256         FIELD_MAP.put('G', ChronoField.ERA);                       // Java, CLDR (different to both for 1/2 chars)
  1451         FIELD_MAP.put('G', ChronoField.ERA);                       // Java, LDML (different to both for 1/2 chars)
  1257         FIELD_MAP.put('y', ChronoField.YEAR);                      // CLDR
  1452         FIELD_MAP.put('y', ChronoField.YEAR);                      // LDML
  1258         // FIELD_MAP.put('y', ChronoField.YEAR_OF_ERA);            // Java, CLDR  // TODO redefine from above
  1453         // FIELD_MAP.put('y', ChronoField.YEAR_OF_ERA);            // Java, LDML  // TODO redefine from above
  1259         // FIELD_MAP.put('u', ChronoField.YEAR);                   // CLDR  // TODO
  1454         // FIELD_MAP.put('u', ChronoField.YEAR);                   // LDML  // TODO
  1260         // FIELD_MAP.put('Y', ISODateTimeField.WEEK_BASED_YEAR);          // Java7, CLDR (needs localized week number)  // TODO
  1455         // FIELD_MAP.put('Y', IsoFields.WEEK_BASED_YEAR);          // Java7, LDML (needs localized week number)  // TODO
  1261         FIELD_MAP.put('Q', ISOFields.QUARTER_OF_YEAR);             // CLDR (removed quarter from 310)
  1456         FIELD_MAP.put('Q', IsoFields.QUARTER_OF_YEAR);             // LDML (removed quarter from 310)
  1262         FIELD_MAP.put('M', ChronoField.MONTH_OF_YEAR);             // Java, CLDR
  1457         FIELD_MAP.put('M', ChronoField.MONTH_OF_YEAR);             // Java, LDML
  1263         // FIELD_MAP.put('w', WeekFields.weekOfYear());            // Java, CLDR (needs localized week number)
  1458         // FIELD_MAP.put('w', WeekFields.weekOfYear());            // Java, LDML (needs localized week number)
  1264         // FIELD_MAP.put('W', WeekFields.weekOfMonth());           // Java, CLDR (needs localized week number)
  1459         // FIELD_MAP.put('W', WeekFields.weekOfMonth());           // Java, LDML (needs localized week number)
  1265         FIELD_MAP.put('D', ChronoField.DAY_OF_YEAR);               // Java, CLDR
  1460         FIELD_MAP.put('D', ChronoField.DAY_OF_YEAR);               // Java, LDML
  1266         FIELD_MAP.put('d', ChronoField.DAY_OF_MONTH);              // Java, CLDR
  1461         FIELD_MAP.put('d', ChronoField.DAY_OF_MONTH);              // Java, LDML
  1267         FIELD_MAP.put('F', ChronoField.ALIGNED_WEEK_OF_MONTH);     // Java, CLDR
  1462         FIELD_MAP.put('F', ChronoField.ALIGNED_WEEK_OF_MONTH);     // Java, LDML
  1268         FIELD_MAP.put('E', ChronoField.DAY_OF_WEEK);               // Java, CLDR (different to both for 1/2 chars)
  1463         FIELD_MAP.put('E', ChronoField.DAY_OF_WEEK);               // Java, LDML (different to both for 1/2 chars)
  1269         // FIELD_MAP.put('e', WeekFields.dayOfWeek());             // CLDR (needs localized week number)
  1464         // FIELD_MAP.put('e', WeekFields.dayOfWeek());             // LDML (needs localized week number)
  1270         FIELD_MAP.put('a', ChronoField.AMPM_OF_DAY);               // Java, CLDR
  1465         FIELD_MAP.put('a', ChronoField.AMPM_OF_DAY);               // Java, LDML
  1271         FIELD_MAP.put('H', ChronoField.HOUR_OF_DAY);               // Java, CLDR
  1466         FIELD_MAP.put('H', ChronoField.HOUR_OF_DAY);               // Java, LDML
  1272         FIELD_MAP.put('k', ChronoField.CLOCK_HOUR_OF_DAY);         // Java, CLDR
  1467         FIELD_MAP.put('k', ChronoField.CLOCK_HOUR_OF_DAY);         // Java, LDML
  1273         FIELD_MAP.put('K', ChronoField.HOUR_OF_AMPM);              // Java, CLDR
  1468         FIELD_MAP.put('K', ChronoField.HOUR_OF_AMPM);              // Java, LDML
  1274         FIELD_MAP.put('h', ChronoField.CLOCK_HOUR_OF_AMPM);        // Java, CLDR
  1469         FIELD_MAP.put('h', ChronoField.CLOCK_HOUR_OF_AMPM);        // Java, LDML
  1275         FIELD_MAP.put('m', ChronoField.MINUTE_OF_HOUR);            // Java, CLDR
  1470         FIELD_MAP.put('m', ChronoField.MINUTE_OF_HOUR);            // Java, LDML
  1276         FIELD_MAP.put('s', ChronoField.SECOND_OF_MINUTE);          // Java, CLDR
  1471         FIELD_MAP.put('s', ChronoField.SECOND_OF_MINUTE);          // Java, LDML
  1277         FIELD_MAP.put('S', ChronoField.NANO_OF_SECOND);            // CLDR (Java uses milli-of-second number)
  1472         FIELD_MAP.put('S', ChronoField.NANO_OF_SECOND);            // LDML (Java uses milli-of-second number)
  1278         FIELD_MAP.put('A', ChronoField.MILLI_OF_DAY);              // CLDR
  1473         FIELD_MAP.put('A', ChronoField.MILLI_OF_DAY);              // LDML
  1279         FIELD_MAP.put('n', ChronoField.NANO_OF_SECOND);            // 310
  1474         FIELD_MAP.put('n', ChronoField.NANO_OF_SECOND);            // 310 (proposed for LDML)
  1280         FIELD_MAP.put('N', ChronoField.NANO_OF_DAY);               // 310
  1475         FIELD_MAP.put('N', ChronoField.NANO_OF_DAY);               // 310 (proposed for LDML)
  1281         // reserved - z,Z,X,I,p
  1476         // 310 - z - time-zone names, matches LDML and SimpleDateFormat 1 to 4
  1282         // Java - X - compatible, but extended to 4 and 5 letters
  1477         // 310 - Z - matches SimpleDateFormat and LDML
  1283         // Java - u - clashes with CLDR, go with CLDR (year-proleptic) here
  1478         // 310 - V - time-zone id, matches proposed LDML
  1284         // CLDR - U - cycle year name, not supported by 310 yet
  1479         // 310 - p - prefix for padding
  1285         // CLDR - l - deprecated
  1480         // 310 - X - matches proposed LDML, almost matches JavaSDF for 1, exact match 2&3, extended 4&5
  1286         // CLDR - j - not relevant
  1481         // 310 - x - matches proposed LDML
  1287         // CLDR - g - modified-julian-day
  1482         // Java - u - clashes with LDML, go with LDML (year-proleptic) here
  1288         // CLDR - z - time-zone names  // TODO properly
  1483         // LDML - U - cycle year name, not supported by 310 yet
  1289         // CLDR - Z - different approach here  // TODO bring 310 in line with CLDR
  1484         // LDML - l - deprecated
  1290         // CLDR - v,V - extended time-zone names
  1485         // LDML - j - not relevant
  1291         // CLDR - q/c/L - standalone quarter/day-of-week/month
  1486         // LDML - g - modified-julian-day
  1292         //  310 - I - time-zone id
  1487         // LDML - v,V - extended time-zone names
  1293         //  310 - p - prefix for padding
  1488         // LDML - q/c/L - standalone quarter/day-of-week/month
  1294     }
  1489     }
  1295 
  1490 
  1296     //-----------------------------------------------------------------------
  1491     //-----------------------------------------------------------------------
  1297     /**
  1492     /**
  1298      * Causes the next added printer/parser to pad to a fixed width using a space.
  1493      * Causes the next added printer/parser to pad to a fixed width using a space.
  1299      * <p>
  1494      * <p>
  1300      * This padding will pad to a fixed width using spaces.
  1495      * This padding will pad to a fixed width using spaces.
  1301      * <p>
  1496      * <p>
  1302      * An exception will be thrown during printing if the pad width
  1497      * During formatting, the decorated element will be output and then padded
  1303      * is exceeded.
  1498      * to the specified width. An exception will be thrown during formatting if
       
  1499      * the pad width is exceeded.
       
  1500      * <p>
       
  1501      * During parsing, the padding and decorated element are parsed.
       
  1502      * If parsing is lenient, then the pad width is treated as a maximum.
       
  1503      * If parsing is case insensitive, then the pad character is matched ignoring case.
       
  1504      * The padding is parsed greedily. Thus, if the decorated element starts with
       
  1505      * the pad character, it will not be parsed.
  1304      *
  1506      *
  1305      * @param padWidth  the pad width, 1 or greater
  1507      * @param padWidth  the pad width, 1 or greater
  1306      * @return this, for chaining, not null
  1508      * @return this, for chaining, not null
  1307      * @throws IllegalArgumentException if pad width is too small
  1509      * @throws IllegalArgumentException if pad width is too small
  1308      */
  1510      */
  1314      * Causes the next added printer/parser to pad to a fixed width.
  1516      * Causes the next added printer/parser to pad to a fixed width.
  1315      * <p>
  1517      * <p>
  1316      * This padding is intended for padding other than zero-padding.
  1518      * This padding is intended for padding other than zero-padding.
  1317      * Zero-padding should be achieved using the appendValue methods.
  1519      * Zero-padding should be achieved using the appendValue methods.
  1318      * <p>
  1520      * <p>
  1319      * An exception will be thrown during printing if the pad width
  1521      * During formatting, the decorated element will be output and then padded
  1320      * is exceeded.
  1522      * to the specified width. An exception will be thrown during formatting if
       
  1523      * the pad width is exceeded.
       
  1524      * <p>
       
  1525      * During parsing, the padding and decorated element are parsed.
       
  1526      * If parsing is lenient, then the pad width is treated as a maximum.
       
  1527      * If parsing is case insensitive, then the pad character is matched ignoring case.
       
  1528      * The padding is parsed greedily. Thus, if the decorated element starts with
       
  1529      * the pad character, it will not be parsed.
  1321      *
  1530      *
  1322      * @param padWidth  the pad width, 1 or greater
  1531      * @param padWidth  the pad width, 1 or greater
  1323      * @param padChar  the pad character
  1532      * @param padChar  the pad character
  1324      * @return this, for chaining, not null
  1533      * @return this, for chaining, not null
  1325      * @throws IllegalArgumentException if pad width is too small
  1534      * @throws IllegalArgumentException if pad width is too small
  1336 
  1545 
  1337     //-----------------------------------------------------------------------
  1546     //-----------------------------------------------------------------------
  1338     /**
  1547     /**
  1339      * Mark the start of an optional section.
  1548      * Mark the start of an optional section.
  1340      * <p>
  1549      * <p>
  1341      * The output of printing can include optional sections, which may be nested.
  1550      * The output of formatting can include optional sections, which may be nested.
  1342      * An optional section is started by calling this method and ended by calling
  1551      * An optional section is started by calling this method and ended by calling
  1343      * {@link #optionalEnd()} or by ending the build process.
  1552      * {@link #optionalEnd()} or by ending the build process.
  1344      * <p>
  1553      * <p>
  1345      * All elements in the optional section are treated as optional.
  1554      * All elements in the optional section are treated as optional.
  1346      * During printing, the section is only output if data is available in the
  1555      * During formatting, the section is only output if data is available in the
  1347      * {@code TemporalAccessor} for all the elements in the section.
  1556      * {@code TemporalAccessor} for all the elements in the section.
  1348      * During parsing, the whole section may be missing from the parsed string.
  1557      * During parsing, the whole section may be missing from the parsed string.
  1349      * <p>
  1558      * <p>
  1350      * For example, consider a builder setup as
  1559      * For example, consider a builder setup as
  1351      * {@code builder.appendValue(HOUR_OF_DAY,2).optionalStart().appendValue(MINUTE_OF_HOUR,2)}.
  1560      * {@code builder.appendValue(HOUR_OF_DAY,2).optionalStart().appendValue(MINUTE_OF_HOUR,2)}.
  1352      * The optional section ends automatically at the end of the builder.
  1561      * The optional section ends automatically at the end of the builder.
  1353      * During printing, the minute will only be output if its value can be obtained from the date-time.
  1562      * During formatting, the minute will only be output if its value can be obtained from the date-time.
  1354      * During parsing, the input will be successfully parsed whether the minute is present or not.
  1563      * During parsing, the input will be successfully parsed whether the minute is present or not.
  1355      *
  1564      *
  1356      * @return this, for chaining, not null
  1565      * @return this, for chaining, not null
  1357      */
  1566      */
  1358     public DateTimeFormatterBuilder optionalStart() {
  1567     public DateTimeFormatterBuilder optionalStart() {
  1362     }
  1571     }
  1363 
  1572 
  1364     /**
  1573     /**
  1365      * Ends an optional section.
  1574      * Ends an optional section.
  1366      * <p>
  1575      * <p>
  1367      * The output of printing can include optional sections, which may be nested.
  1576      * The output of formatting can include optional sections, which may be nested.
  1368      * An optional section is started by calling {@link #optionalStart()} and ended
  1577      * An optional section is started by calling {@link #optionalStart()} and ended
  1369      * using this method (or at the end of the builder).
  1578      * using this method (or at the end of the builder).
  1370      * <p>
  1579      * <p>
  1371      * Calling this method without having previously called {@code optionalStart}
  1580      * Calling this method without having previously called {@code optionalStart}
  1372      * will throw an exception.
  1581      * will throw an exception.
  1373      * Calling this method immediately after calling {@code optionalStart} has no effect
  1582      * Calling this method immediately after calling {@code optionalStart} has no effect
  1374      * on the formatter other than ending the (empty) optional section.
  1583      * on the formatter other than ending the (empty) optional section.
  1375      * <p>
  1584      * <p>
  1376      * All elements in the optional section are treated as optional.
  1585      * All elements in the optional section are treated as optional.
  1377      * During printing, the section is only output if data is available in the
  1586      * During formatting, the section is only output if data is available in the
  1378      * {@code TemporalAccessor} for all the elements in the section.
  1587      * {@code TemporalAccessor} for all the elements in the section.
  1379      * During parsing, the whole section may be missing from the parsed string.
  1588      * During parsing, the whole section may be missing from the parsed string.
  1380      * <p>
  1589      * <p>
  1381      * For example, consider a builder setup as
  1590      * For example, consider a builder setup as
  1382      * {@code builder.appendValue(HOUR_OF_DAY,2).optionalStart().appendValue(MINUTE_OF_HOUR,2).optionalEnd()}.
  1591      * {@code builder.appendValue(HOUR_OF_DAY,2).optionalStart().appendValue(MINUTE_OF_HOUR,2).optionalEnd()}.
  1383      * During printing, the minute will only be output if its value can be obtained from the date-time.
  1592      * During formatting, the minute will only be output if its value can be obtained from the date-time.
  1384      * During parsing, the input will be successfully parsed whether the minute is present or not.
  1593      * During parsing, the input will be successfully parsed whether the minute is present or not.
  1385      *
  1594      *
  1386      * @return this, for chaining, not null
  1595      * @return this, for chaining, not null
  1387      * @throws IllegalStateException if there was no previous call to {@code optionalStart}
  1596      * @throws IllegalStateException if there was no previous call to {@code optionalStart}
  1388      */
  1597      */
  1464         return new DateTimeFormatter(pp, locale, DateTimeFormatSymbols.STANDARD, null, null);
  1673         return new DateTimeFormatter(pp, locale, DateTimeFormatSymbols.STANDARD, null, null);
  1465     }
  1674     }
  1466 
  1675 
  1467     //-----------------------------------------------------------------------
  1676     //-----------------------------------------------------------------------
  1468     /**
  1677     /**
  1469      * Strategy for printing/parsing date-time information.
  1678      * Strategy for formatting/parsing date-time information.
  1470      * <p>
  1679      * <p>
  1471      * The printer may print any part, or the whole, of the input date-time object.
  1680      * The printer may format any part, or the whole, of the input date-time object.
  1472      * Typically, a complete print is constructed from a number of smaller
  1681      * Typically, a complete format is constructed from a number of smaller
  1473      * units, each outputting a single field.
  1682      * units, each outputting a single field.
  1474      * <p>
  1683      * <p>
  1475      * The parser may parse any piece of text from the input, storing the result
  1684      * The parser may parse any piece of text from the input, storing the result
  1476      * in the context. Typically, each individual parser will just parse one
  1685      * in the context. Typically, each individual parser will just parse one
  1477      * field, such as the day-of-month, storing the value in the context.
  1686      * field, such as the day-of-month, storing the value in the context.
  1478      * Once the parse is complete, the caller will then convert the context
  1687      * Once the parse is complete, the caller will then resolve the parsed values
  1479      * to a {@link DateTimeBuilder} to merge the parsed values to create the
  1688      * to create the desired object, such as a {@code LocalDate}.
  1480      * desired object, such as a {@code LocalDate}.
       
  1481      * <p>
  1689      * <p>
  1482      * The parse position will be updated during the parse. Parsing will start at
  1690      * The parse position will be updated during the parse. Parsing will start at
  1483      * the specified index and the return value specifies the new parse position
  1691      * the specified index and the return value specifies the new parse position
  1484      * for the next parser. If an error occurs, the returned index will be negative
  1692      * for the next parser. If an error occurs, the returned index will be negative
  1485      * and will have the error position encoded using the complement operator.
  1693      * and will have the error position encoded using the complement operator.
  1487      * <h3>Specification for implementors</h3>
  1695      * <h3>Specification for implementors</h3>
  1488      * This interface must be implemented with care to ensure other classes operate correctly.
  1696      * This interface must be implemented with care to ensure other classes operate correctly.
  1489      * All implementations that can be instantiated must be final, immutable and thread-safe.
  1697      * All implementations that can be instantiated must be final, immutable and thread-safe.
  1490      * <p>
  1698      * <p>
  1491      * The context is not a thread-safe object and a new instance will be created
  1699      * The context is not a thread-safe object and a new instance will be created
  1492      * for each print that occurs. The context must not be stored in an instance
  1700      * for each format that occurs. The context must not be stored in an instance
  1493      * variable or shared with any other threads.
  1701      * variable or shared with any other threads.
  1494      */
  1702      */
  1495     interface DateTimePrinterParser {
  1703     interface DateTimePrinterParser {
  1496 
  1704 
  1497         /**
  1705         /**
  1498          * Prints the date-time object to the buffer.
  1706          * Prints the date-time object to the buffer.
  1499          * <p>
  1707          * <p>
  1500          * The context holds information to use during the print.
  1708          * The context holds information to use during the format.
  1501          * It also contains the date-time information to be printed.
  1709          * It also contains the date-time information to be printed.
  1502          * <p>
  1710          * <p>
  1503          * The buffer must not be mutated beyond the content controlled by the implementation.
  1711          * The buffer must not be mutated beyond the content controlled by the implementation.
  1504          *
  1712          *
  1505          * @param context  the context to print using, not null
  1713          * @param context  the context to format using, not null
  1506          * @param buf  the buffer to append to, not null
  1714          * @param buf  the buffer to append to, not null
  1507          * @return false if unable to query the value from the date-time, true otherwise
  1715          * @return false if unable to query the value from the date-time, true otherwise
  1508          * @throws DateTimeException if the date-time cannot be printed successfully
  1716          * @throws DateTimeException if the date-time cannot be printed successfully
  1509          */
  1717          */
  1510         boolean print(DateTimePrintContext context, StringBuilder buf);
  1718         boolean format(DateTimePrintContext context, StringBuilder buf);
  1511 
  1719 
  1512         /**
  1720         /**
  1513          * Parses text into date-time information.
  1721          * Parses text into date-time information.
  1514          * <p>
  1722          * <p>
  1515          * The context holds information to use during the parse.
  1723          * The context holds information to use during the parse.
  1555             }
  1763             }
  1556             return new CompositePrinterParser(printerParsers, optional);
  1764             return new CompositePrinterParser(printerParsers, optional);
  1557         }
  1765         }
  1558 
  1766 
  1559         @Override
  1767         @Override
  1560         public boolean print(DateTimePrintContext context, StringBuilder buf) {
  1768         public boolean format(DateTimePrintContext context, StringBuilder buf) {
  1561             int length = buf.length();
  1769             int length = buf.length();
  1562             if (optional) {
  1770             if (optional) {
  1563                 context.startOptional();
  1771                 context.startOptional();
  1564             }
  1772             }
  1565             try {
  1773             try {
  1566                 for (DateTimePrinterParser pp : printerParsers) {
  1774                 for (DateTimePrinterParser pp : printerParsers) {
  1567                     if (pp.print(context, buf) == false) {
  1775                     if (pp.format(context, buf) == false) {
  1568                         buf.setLength(length);  // reset buffer
  1776                         buf.setLength(length);  // reset buffer
  1569                         return true;
  1777                         return true;
  1570                     }
  1778                     }
  1571                 }
  1779                 }
  1572             } finally {
  1780             } finally {
  1638             this.padWidth = padWidth;
  1846             this.padWidth = padWidth;
  1639             this.padChar = padChar;
  1847             this.padChar = padChar;
  1640         }
  1848         }
  1641 
  1849 
  1642         @Override
  1850         @Override
  1643         public boolean print(DateTimePrintContext context, StringBuilder buf) {
  1851         public boolean format(DateTimePrintContext context, StringBuilder buf) {
  1644             int preLen = buf.length();
  1852             int preLen = buf.length();
  1645             if (printerParser.print(context, buf) == false) {
  1853             if (printerParser.format(context, buf) == false) {
  1646                 return false;
  1854                 return false;
  1647             }
  1855             }
  1648             int len = buf.length() - preLen;
  1856             int len = buf.length() - preLen;
  1649             if (len > padWidth) {
  1857             if (len > padWidth) {
  1650                 throw new DateTimePrintException(
  1858                 throw new DateTimeException(
  1651                     "Cannot print as output of " + len + " characters exceeds pad width of " + padWidth);
  1859                     "Cannot print as output of " + len + " characters exceeds pad width of " + padWidth);
  1652             }
  1860             }
  1653             for (int i = 0; i < padWidth - len; i++) {
  1861             for (int i = 0; i < padWidth - len; i++) {
  1654                 buf.insert(preLen, padChar);
  1862                 buf.insert(preLen, padChar);
  1655             }
  1863             }
  1656             return true;
  1864             return true;
  1657         }
  1865         }
  1658 
  1866 
  1659         @Override
  1867         @Override
  1660         public int parse(DateTimeParseContext context, CharSequence text, int position) {
  1868         public int parse(DateTimeParseContext context, CharSequence text, int position) {
       
  1869             // cache context before changed by decorated parser
       
  1870             final boolean strict = context.isStrict();
       
  1871             // parse
  1661             if (position > text.length()) {
  1872             if (position > text.length()) {
  1662                 throw new IndexOutOfBoundsException();
  1873                 throw new IndexOutOfBoundsException();
  1663             }
  1874             }
       
  1875             if (position == text.length()) {
       
  1876                 return ~position;  // no more characters in the string
       
  1877             }
  1664             int endPos = position + padWidth;
  1878             int endPos = position + padWidth;
  1665             if (endPos > text.length()) {
  1879             if (endPos > text.length()) {
  1666                 return ~position;  // not enough characters in the string to meet the parse width
  1880                 if (strict) {
       
  1881                     return ~position;  // not enough characters in the string to meet the parse width
       
  1882                 }
       
  1883                 endPos = text.length();
  1667             }
  1884             }
  1668             int pos = position;
  1885             int pos = position;
  1669             while (pos < endPos && text.charAt(pos) == padChar) {
  1886             while (pos < endPos && context.charEquals(text.charAt(pos), padChar)) {
  1670                 pos++;
  1887                 pos++;
  1671             }
  1888             }
  1672             text = text.subSequence(0, endPos);
  1889             text = text.subSequence(0, endPos);
  1673             int firstError = 0;
  1890             int resultPos = printerParser.parse(context, text, pos);
  1674             while (pos >= position) {
  1891             if (resultPos != endPos && strict) {
  1675                 int resultPos = printerParser.parse(context, text, pos);
  1892                 return ~(position + pos);  // parse of decorated field didn't parse to the end
  1676                 if (resultPos < 0) {
  1893             }
  1677                     // parse of decorated field had an error
  1894             return resultPos;
  1678                     if (firstError == 0) {
       
  1679                         firstError = resultPos;
       
  1680                     }
       
  1681                     // loop around in case the decorated parser can handle the padChar at the start
       
  1682                     pos--;
       
  1683                     continue;
       
  1684                 }
       
  1685                 if (resultPos != endPos) {
       
  1686                     return ~position;  // parse of decorated field didn't parse to the end
       
  1687                 }
       
  1688                 return resultPos;
       
  1689             }
       
  1690             // loop runs at least once, so firstError must be set by the time we get here
       
  1691             return firstError;  // return error from first parse of decorated field
       
  1692         }
  1895         }
  1693 
  1896 
  1694         @Override
  1897         @Override
  1695         public String toString() {
  1898         public String toString() {
  1696             return "Pad(" + printerParser + "," + padWidth + (padChar == ' ' ? ")" : ",'" + padChar + "')");
  1899             return "Pad(" + printerParser + "," + padWidth + (padChar == ' ' ? ")" : ",'" + padChar + "')");
  1706         INSENSITIVE,
  1909         INSENSITIVE,
  1707         STRICT,
  1910         STRICT,
  1708         LENIENT;
  1911         LENIENT;
  1709 
  1912 
  1710         @Override
  1913         @Override
  1711         public boolean print(DateTimePrintContext context, StringBuilder buf) {
  1914         public boolean format(DateTimePrintContext context, StringBuilder buf) {
  1712             return true;  // nothing to do here
  1915             return true;  // nothing to do here
  1713         }
  1916         }
  1714 
  1917 
  1715         @Override
  1918         @Override
  1716         public int parse(DateTimeParseContext context, CharSequence text, int position) {
  1919         public int parse(DateTimeParseContext context, CharSequence text, int position) {
  1747         CharLiteralPrinterParser(char literal) {
  1950         CharLiteralPrinterParser(char literal) {
  1748             this.literal = literal;
  1951             this.literal = literal;
  1749         }
  1952         }
  1750 
  1953 
  1751         @Override
  1954         @Override
  1752         public boolean print(DateTimePrintContext context, StringBuilder buf) {
  1955         public boolean format(DateTimePrintContext context, StringBuilder buf) {
  1753             buf.append(literal);
  1956             buf.append(literal);
  1754             return true;
  1957             return true;
  1755         }
  1958         }
  1756 
  1959 
  1757         @Override
  1960         @Override
  1790         StringLiteralPrinterParser(String literal) {
  1993         StringLiteralPrinterParser(String literal) {
  1791             this.literal = literal;  // validated by caller
  1994             this.literal = literal;  // validated by caller
  1792         }
  1995         }
  1793 
  1996 
  1794         @Override
  1997         @Override
  1795         public boolean print(DateTimePrintContext context, StringBuilder buf) {
  1998         public boolean format(DateTimePrintContext context, StringBuilder buf) {
  1796             buf.append(literal);
  1999             buf.append(literal);
  1797             return true;
  2000             return true;
  1798         }
  2001         }
  1799 
  2002 
  1800         @Override
  2003         @Override
  1845         private final int subsequentWidth;
  2048         private final int subsequentWidth;
  1846 
  2049 
  1847         /**
  2050         /**
  1848          * Constructor.
  2051          * Constructor.
  1849          *
  2052          *
  1850          * @param field  the field to print, not null
  2053          * @param field  the field to format, not null
  1851          * @param minWidth  the minimum field width, from 1 to 19
  2054          * @param minWidth  the minimum field width, from 1 to 19
  1852          * @param maxWidth  the maximum field width, from minWidth to 19
  2055          * @param maxWidth  the maximum field width, from minWidth to 19
  1853          * @param signStyle  the positive/negative sign style, not null
  2056          * @param signStyle  the positive/negative sign style, not null
  1854          */
  2057          */
  1855         NumberPrinterParser(TemporalField field, int minWidth, int maxWidth, SignStyle signStyle) {
  2058         NumberPrinterParser(TemporalField field, int minWidth, int maxWidth, SignStyle signStyle) {
  1862         }
  2065         }
  1863 
  2066 
  1864         /**
  2067         /**
  1865          * Constructor.
  2068          * Constructor.
  1866          *
  2069          *
  1867          * @param field  the field to print, not null
  2070          * @param field  the field to format, not null
  1868          * @param minWidth  the minimum field width, from 1 to 19
  2071          * @param minWidth  the minimum field width, from 1 to 19
  1869          * @param maxWidth  the maximum field width, from minWidth to 19
  2072          * @param maxWidth  the maximum field width, from minWidth to 19
  1870          * @param signStyle  the positive/negative sign style, not null
  2073          * @param signStyle  the positive/negative sign style, not null
  1871          * @param subsequentWidth  the width of subsequent non-negative numbers, 0 or greater,
  2074          * @param subsequentWidth  the width of subsequent non-negative numbers, 0 or greater,
  1872          *  -1 if fixed width due to active adjacent parsing
  2075          *  -1 if fixed width due to active adjacent parsing
  1898         NumberPrinterParser withSubsequentWidth(int subsequentWidth) {
  2101         NumberPrinterParser withSubsequentWidth(int subsequentWidth) {
  1899             return new NumberPrinterParser(field, minWidth, maxWidth, signStyle, this.subsequentWidth + subsequentWidth);
  2102             return new NumberPrinterParser(field, minWidth, maxWidth, signStyle, this.subsequentWidth + subsequentWidth);
  1900         }
  2103         }
  1901 
  2104 
  1902         @Override
  2105         @Override
  1903         public boolean print(DateTimePrintContext context, StringBuilder buf) {
  2106         public boolean format(DateTimePrintContext context, StringBuilder buf) {
  1904             Long valueLong = context.getValue(field);
  2107             Chronology chrono = context.getTemporal().query(Queries.chronology());
       
  2108             Long valueLong;
       
  2109             if (chrono == JapaneseChronology.INSTANCE && field == ChronoField.YEAR) {
       
  2110                 valueLong = context.getValue(ChronoField.YEAR_OF_ERA);
       
  2111             } else {
       
  2112                 valueLong = context.getValue(field);
       
  2113             }
  1905             if (valueLong == null) {
  2114             if (valueLong == null) {
  1906                 return false;
  2115                 return false;
  1907             }
  2116             }
  1908             long value = getValue(valueLong);
  2117             long value = getValue(valueLong);
  1909             DateTimeFormatSymbols symbols = context.getSymbols();
  2118             DateTimeFormatSymbols symbols = context.getSymbols();
  1910             String str = (value == Long.MIN_VALUE ? "9223372036854775808" : Long.toString(Math.abs(value)));
  2119             String str = (value == Long.MIN_VALUE ? "9223372036854775808" : Long.toString(Math.abs(value)));
  1911             if (str.length() > maxWidth) {
  2120             if (str.length() > maxWidth) {
  1912                 throw new DateTimePrintException("Field " + field.getName() +
  2121                 throw new DateTimeException("Field " + field.getName() +
  1913                     " cannot be printed as the value " + value +
  2122                     " cannot be printed as the value " + value +
  1914                     " exceeds the maximum print width of " + maxWidth);
  2123                     " exceeds the maximum print width of " + maxWidth);
  1915             }
  2124             }
  1916             str = symbols.convertNumberToI18N(str);
  2125             str = symbols.convertNumberToI18N(str);
  1917 
  2126 
  1932                     case EXCEEDS_PAD:
  2141                     case EXCEEDS_PAD:
  1933                     case ALWAYS:
  2142                     case ALWAYS:
  1934                         buf.append(symbols.getNegativeSign());
  2143                         buf.append(symbols.getNegativeSign());
  1935                         break;
  2144                         break;
  1936                     case NOT_NEGATIVE:
  2145                     case NOT_NEGATIVE:
  1937                         throw new DateTimePrintException("Field " + field.getName() +
  2146                         throw new DateTimeException("Field " + field.getName() +
  1938                             " cannot be printed as the value " + value +
  2147                             " cannot be printed as the value " + value +
  1939                             " cannot be negative according to the SignStyle");
  2148                             " cannot be negative according to the SignStyle");
  1940                 }
  2149                 }
  1941             }
  2150             }
  1942             for (int i = 0; i < minWidth - str.length(); i++) {
  2151             for (int i = 0; i < minWidth - str.length(); i++) {
  2055                 if (totalBig.bitLength() > 63) {
  2264                 if (totalBig.bitLength() > 63) {
  2056                     // overflow, parse 1 less digit
  2265                     // overflow, parse 1 less digit
  2057                     totalBig = totalBig.divide(BigInteger.TEN);
  2266                     totalBig = totalBig.divide(BigInteger.TEN);
  2058                     pos--;
  2267                     pos--;
  2059                 }
  2268                 }
  2060                 setValue(context, totalBig.longValue());
  2269                 return setValue(context, totalBig.longValue(), position, pos);
  2061             } else {
  2270             }
  2062                 setValue(context, total);
  2271             return setValue(context, total, position, pos);
  2063             }
       
  2064             return pos;
       
  2065         }
  2272         }
  2066 
  2273 
  2067         /**
  2274         /**
  2068          * Stores the value.
  2275          * Stores the value.
  2069          *
  2276          *
  2070          * @param context  the context to store into, not null
  2277          * @param context  the context to store into, not null
  2071          * @param value  the value
  2278          * @param value  the value
       
  2279          * @param errorPos  the position of the field being parsed
       
  2280          * @param successPos  the position after the field being parsed
       
  2281          * @return the new position
  2072          */
  2282          */
  2073         void setValue(DateTimeParseContext context, long value) {
  2283         int setValue(DateTimeParseContext context, long value, int errorPos, int successPos) {
  2074             context.setParsedField(field, value);
  2284             TemporalField f = field;
       
  2285             if (field == ChronoField.YEAR) {
       
  2286                 Chronology chrono = context.getEffectiveChronology();
       
  2287                 if (chrono == JapaneseChronology.INSTANCE) {
       
  2288                     f = ChronoField.YEAR_OF_ERA;
       
  2289                 }
       
  2290             }
       
  2291             return context.setParsedField(f, value, errorPos, successPos);
  2075         }
  2292         }
  2076 
  2293 
  2077         @Override
  2294         @Override
  2078         public String toString() {
  2295         public String toString() {
  2079             if (minWidth == 1 && maxWidth == 19 && signStyle == SignStyle.NORMAL) {
  2296             if (minWidth == 1 && maxWidth == 19 && signStyle == SignStyle.NORMAL) {
  2095         private final int range;
  2312         private final int range;
  2096 
  2313 
  2097         /**
  2314         /**
  2098          * Constructor.
  2315          * Constructor.
  2099          *
  2316          *
  2100          * @param field  the field to print, validated not null
  2317          * @param field  the field to format, validated not null
  2101          * @param width  the field width, from 1 to 18
  2318          * @param width  the field width, from 1 to 18
  2102          * @param baseValue  the base value
  2319          * @param baseValue  the base value
  2103          */
  2320          */
  2104         ReducedPrinterParser(TemporalField field, int width, int baseValue) {
  2321         ReducedPrinterParser(TemporalField field, int width, int baseValue) {
  2105             super(field, width, width, SignStyle.NOT_NEGATIVE);
  2322             super(field, width, width, SignStyle.NOT_NEGATIVE);
  2120         long getValue(long value) {
  2337         long getValue(long value) {
  2121             return Math.abs(value % range);
  2338             return Math.abs(value % range);
  2122         }
  2339         }
  2123 
  2340 
  2124         @Override
  2341         @Override
  2125         void setValue(DateTimeParseContext context, long value) {
  2342         int setValue(DateTimeParseContext context, long value, int errorPos, int successPos) {
  2126             int lastPart = baseValue % range;
  2343             int lastPart = baseValue % range;
  2127             if (baseValue > 0) {
  2344             if (baseValue > 0) {
  2128                 value = baseValue - lastPart + value;
  2345                 value = baseValue - lastPart + value;
  2129             } else {
  2346             } else {
  2130                 value = baseValue - lastPart - value;
  2347                 value = baseValue - lastPart - value;
  2131             }
  2348             }
  2132             if (value < baseValue) {
  2349             if (value < baseValue) {
  2133                 value += range;
  2350                 value += range;
  2134             }
  2351             }
  2135             context.setParsedField(field, value);
  2352             return context.setParsedField(field, value, errorPos, successPos);
  2136         }
  2353         }
  2137 
  2354 
  2138         @Override
  2355         @Override
  2139         NumberPrinterParser withFixedWidth() {
  2356         NumberPrinterParser withFixedWidth() {
  2140             return this;
  2357             return this;
  2189             this.maxWidth = maxWidth;
  2406             this.maxWidth = maxWidth;
  2190             this.decimalPoint = decimalPoint;
  2407             this.decimalPoint = decimalPoint;
  2191         }
  2408         }
  2192 
  2409 
  2193         @Override
  2410         @Override
  2194         public boolean print(DateTimePrintContext context, StringBuilder buf) {
  2411         public boolean format(DateTimePrintContext context, StringBuilder buf) {
  2195             Long value = context.getValue(field);
  2412             Long value = context.getValue(field);
  2196             if (value == null) {
  2413             if (value == null) {
  2197                 return false;
  2414                 return false;
  2198             }
  2415             }
  2199             DateTimeFormatSymbols symbols = context.getSymbols();
  2416             DateTimeFormatSymbols symbols = context.getSymbols();
  2255                 }
  2472                 }
  2256                 total = total * 10 + digit;
  2473                 total = total * 10 + digit;
  2257             }
  2474             }
  2258             BigDecimal fraction = new BigDecimal(total).movePointLeft(pos - position);
  2475             BigDecimal fraction = new BigDecimal(total).movePointLeft(pos - position);
  2259             long value = convertFromFraction(fraction);
  2476             long value = convertFromFraction(fraction);
  2260             context.setParsedField(field, value);
  2477             return context.setParsedField(field, value, position, pos);
  2261             return pos;
       
  2262         }
  2478         }
  2263 
  2479 
  2264         /**
  2480         /**
  2265          * Converts a value for this field to a fraction between 0 and 1.
  2481          * Converts a value for this field to a fraction between 0 and 1.
  2266          * <p>
  2482          * <p>
  2346             this.textStyle = textStyle;
  2562             this.textStyle = textStyle;
  2347             this.provider = provider;
  2563             this.provider = provider;
  2348         }
  2564         }
  2349 
  2565 
  2350         @Override
  2566         @Override
  2351         public boolean print(DateTimePrintContext context, StringBuilder buf) {
  2567         public boolean format(DateTimePrintContext context, StringBuilder buf) {
  2352             Long value = context.getValue(field);
  2568             Long value = context.getValue(field);
  2353             if (value == null) {
  2569             if (value == null) {
  2354                 return false;
  2570                 return false;
  2355             }
  2571             }
  2356             String text = null;
  2572             String text;
  2357             if (field == ChronoField.ERA) {
  2573             Chronology chrono = context.getTemporal().query(Queries.chronology());
  2358                 Chrono chrono = context.getTemporal().query(Queries.chrono());
  2574             if (chrono == null || chrono == IsoChronology.INSTANCE) {
  2359                 if (chrono == null) {
  2575                 text = provider.getText(field, value, textStyle, context.getLocale());
  2360                     chrono = ISOChrono.INSTANCE;
       
  2361                 }
       
  2362                 text = provider.getEraText(chrono, value, textStyle, context.getLocale());
       
  2363             } else {
  2576             } else {
  2364                 text = provider.getText(field, value, textStyle, context.getLocale());
  2577                 text = provider.getText(chrono, field, value, textStyle, context.getLocale());
  2365             }
  2578             }
  2366             if (text == null) {
  2579             if (text == null) {
  2367                 return numberPrinterParser().print(context, buf);
  2580                 return numberPrinterParser().format(context, buf);
  2368             }
  2581             }
  2369             buf.append(text);
  2582             buf.append(text);
  2370             return true;
  2583             return true;
  2371         }
  2584         }
  2372 
  2585 
  2375             int length = parseText.length();
  2588             int length = parseText.length();
  2376             if (position < 0 || position > length) {
  2589             if (position < 0 || position > length) {
  2377                 throw new IndexOutOfBoundsException();
  2590                 throw new IndexOutOfBoundsException();
  2378             }
  2591             }
  2379             TextStyle style = (context.isStrict() ? textStyle : null);
  2592             TextStyle style = (context.isStrict() ? textStyle : null);
  2380             Iterator<Entry<String, Long>> it = provider.getTextIterator(field, style, context.getLocale());
  2593             Chronology chrono = context.getEffectiveChronology();
       
  2594             Iterator<Entry<String, Long>> it;
       
  2595             if (chrono == null || chrono == IsoChronology.INSTANCE) {
       
  2596                 it = provider.getTextIterator(field, style, context.getLocale());
       
  2597             } else {
       
  2598                 it = provider.getTextIterator(chrono, field, style, context.getLocale());
       
  2599             }
  2381             if (it != null) {
  2600             if (it != null) {
  2382                 while (it.hasNext()) {
  2601                 while (it.hasNext()) {
  2383                     Entry<String, Long> entry = it.next();
  2602                     Entry<String, Long> entry = it.next();
  2384                     String itText = entry.getKey();
  2603                     String itText = entry.getKey();
  2385                     if (context.subSequenceEquals(itText, 0, parseText, position, itText.length())) {
  2604                     if (context.subSequenceEquals(itText, 0, parseText, position, itText.length())) {
  2386                         context.setParsedField(field, entry.getValue());
  2605                         return context.setParsedField(field, entry.getValue(), position, position + itText.length());
  2387                         return position + itText.length();
       
  2388                     }
  2606                     }
  2389                 }
  2607                 }
  2390                 if (context.isStrict()) {
  2608                 if (context.isStrict()) {
  2391                     return ~position;
  2609                     return ~position;
  2392                 }
  2610                 }
  2424         // seconds per day = 86400
  2642         // seconds per day = 86400
  2425         private static final long SECONDS_PER_10000_YEARS = 146097L * 25L * 86400L;
  2643         private static final long SECONDS_PER_10000_YEARS = 146097L * 25L * 86400L;
  2426         private static final long SECONDS_0000_TO_1970 = ((146097L * 5L) - (30L * 365L + 7L)) * 86400L;
  2644         private static final long SECONDS_0000_TO_1970 = ((146097L * 5L) - (30L * 365L + 7L)) * 86400L;
  2427         private static final CompositePrinterParser PARSER = new DateTimeFormatterBuilder()
  2645         private static final CompositePrinterParser PARSER = new DateTimeFormatterBuilder()
  2428                     .parseCaseInsensitive()
  2646                     .parseCaseInsensitive()
  2429                     .append(DateTimeFormatters.isoLocalDate()).appendLiteral('T')
  2647                     .append(DateTimeFormatter.ISO_LOCAL_DATE).appendLiteral('T')
  2430                     .append(DateTimeFormatters.isoLocalTime()).appendLiteral('Z')
  2648                     .append(DateTimeFormatter.ISO_LOCAL_TIME).appendLiteral('Z')
  2431                     .toFormatter().toPrinterParser(false);
  2649                     .toFormatter().toPrinterParser(false);
  2432 
  2650 
  2433         InstantPrinterParser() {
  2651         InstantPrinterParser() {
  2434         }
  2652         }
  2435 
  2653 
  2436         @Override
  2654         @Override
  2437         public boolean print(DateTimePrintContext context, StringBuilder buf) {
  2655         public boolean format(DateTimePrintContext context, StringBuilder buf) {
  2438             // use INSTANT_SECONDS, thus this code is not bound by Instant.MAX
  2656             // use INSTANT_SECONDS, thus this code is not bound by Instant.MAX
  2439             Long inSecs = context.getValue(INSTANT_SECONDS);
  2657             Long inSecs = context.getValue(INSTANT_SECONDS);
  2440             Long inNanos = context.getValue(NANO_OF_SECOND);
  2658             Long inNanos = context.getValue(NANO_OF_SECOND);
  2441             if (inSecs == null || inNanos == null) {
  2659             if (inSecs == null || inNanos == null) {
  2442                 return false;
  2660                 return false;
  2500                 instantSecs = ldt.toEpochSecond(ZoneOffset.UTC);
  2718                 instantSecs = ldt.toEpochSecond(ZoneOffset.UTC);
  2501                 instantSecs += Math.multiplyExact(yearParsed / 10_000L, SECONDS_PER_10000_YEARS);
  2719                 instantSecs += Math.multiplyExact(yearParsed / 10_000L, SECONDS_PER_10000_YEARS);
  2502             } catch (RuntimeException ex) {
  2720             } catch (RuntimeException ex) {
  2503                 return ~position;
  2721                 return ~position;
  2504             }
  2722             }
  2505             context.setParsedField(INSTANT_SECONDS, instantSecs);
  2723             int successPos = text.length();
  2506             context.setParsedField(NANO_OF_SECOND, nano);
  2724             successPos = context.setParsedField(INSTANT_SECONDS, instantSecs, position, successPos);
  2507             return text.length();
  2725             return context.setParsedField(NANO_OF_SECOND, nano, position, successPos);
  2508         }
  2726         }
  2509 
  2727 
  2510         @Override
  2728         @Override
  2511         public String toString() {
  2729         public String toString() {
  2512             return "Instant()";
  2730             return "Instant()";
  2517     /**
  2735     /**
  2518      * Prints or parses an offset ID.
  2736      * Prints or parses an offset ID.
  2519      */
  2737      */
  2520     static final class OffsetIdPrinterParser implements DateTimePrinterParser {
  2738     static final class OffsetIdPrinterParser implements DateTimePrinterParser {
  2521         static final String[] PATTERNS = new String[] {
  2739         static final String[] PATTERNS = new String[] {
  2522             "+HH", "+HHMM", "+HH:MM", "+HHMMss", "+HH:MM:ss", "+HHMMSS", "+HH:MM:SS",
  2740             "+HH", "+HHmm", "+HH:mm", "+HHMM", "+HH:MM", "+HHMMss", "+HH:MM:ss", "+HHMMSS", "+HH:MM:SS",
  2523         };  // order used in pattern builder
  2741         };  // order used in pattern builder
  2524         static final OffsetIdPrinterParser INSTANCE_ID = new OffsetIdPrinterParser("Z", "+HH:MM:ss");
  2742         static final OffsetIdPrinterParser INSTANCE_ID_Z = new OffsetIdPrinterParser("+HH:MM:ss", "Z");
       
  2743         static final OffsetIdPrinterParser INSTANCE_ID_ZERO = new OffsetIdPrinterParser("+HH:MM:ss", "0");
  2525 
  2744 
  2526         private final String noOffsetText;
  2745         private final String noOffsetText;
  2527         private final int type;
  2746         private final int type;
  2528 
  2747 
  2529         /**
  2748         /**
  2530          * Constructor.
  2749          * Constructor.
  2531          *
  2750          *
       
  2751          * @param pattern  the pattern
  2532          * @param noOffsetText  the text to use for UTC, not null
  2752          * @param noOffsetText  the text to use for UTC, not null
  2533          * @param pattern  the pattern
       
  2534          */
  2753          */
  2535         OffsetIdPrinterParser(String noOffsetText, String pattern) {
  2754         OffsetIdPrinterParser(String pattern, String noOffsetText) {
       
  2755             Objects.requireNonNull(pattern, "pattern");
  2536             Objects.requireNonNull(noOffsetText, "noOffsetText");
  2756             Objects.requireNonNull(noOffsetText, "noOffsetText");
  2537             Objects.requireNonNull(pattern, "pattern");
  2757             this.type = checkPattern(pattern);
  2538             this.noOffsetText = noOffsetText;
  2758             this.noOffsetText = noOffsetText;
  2539             this.type = checkPattern(pattern);
       
  2540         }
  2759         }
  2541 
  2760 
  2542         private int checkPattern(String pattern) {
  2761         private int checkPattern(String pattern) {
  2543             for (int i = 0; i < PATTERNS.length; i++) {
  2762             for (int i = 0; i < PATTERNS.length; i++) {
  2544                 if (PATTERNS[i].equals(pattern)) {
  2763                 if (PATTERNS[i].equals(pattern)) {
  2547             }
  2766             }
  2548             throw new IllegalArgumentException("Invalid zone offset pattern: " + pattern);
  2767             throw new IllegalArgumentException("Invalid zone offset pattern: " + pattern);
  2549         }
  2768         }
  2550 
  2769 
  2551         @Override
  2770         @Override
  2552         public boolean print(DateTimePrintContext context, StringBuilder buf) {
  2771         public boolean format(DateTimePrintContext context, StringBuilder buf) {
  2553             Long offsetSecs = context.getValue(OFFSET_SECONDS);
  2772             Long offsetSecs = context.getValue(OFFSET_SECONDS);
  2554             if (offsetSecs == null) {
  2773             if (offsetSecs == null) {
  2555                 return false;
  2774                 return false;
  2556             }
  2775             }
  2557             int totalSecs = Math.toIntExact(offsetSecs);
  2776             int totalSecs = Math.toIntExact(offsetSecs);
  2559                 buf.append(noOffsetText);
  2778                 buf.append(noOffsetText);
  2560             } else {
  2779             } else {
  2561                 int absHours = Math.abs((totalSecs / 3600) % 100);  // anything larger than 99 silently dropped
  2780                 int absHours = Math.abs((totalSecs / 3600) % 100);  // anything larger than 99 silently dropped
  2562                 int absMinutes = Math.abs((totalSecs / 60) % 60);
  2781                 int absMinutes = Math.abs((totalSecs / 60) % 60);
  2563                 int absSeconds = Math.abs(totalSecs % 60);
  2782                 int absSeconds = Math.abs(totalSecs % 60);
       
  2783                 int bufPos = buf.length();
       
  2784                 int output = absHours;
  2564                 buf.append(totalSecs < 0 ? "-" : "+")
  2785                 buf.append(totalSecs < 0 ? "-" : "+")
  2565                     .append((char) (absHours / 10 + '0')).append((char) (absHours % 10 + '0'));
  2786                     .append((char) (absHours / 10 + '0')).append((char) (absHours % 10 + '0'));
  2566                 if (type >= 1) {
  2787                 if (type >= 3 || (type >= 1 && absMinutes > 0)) {
  2567                     buf.append((type % 2) == 0 ? ":" : "")
  2788                     buf.append((type % 2) == 0 ? ":" : "")
  2568                         .append((char) (absMinutes / 10 + '0')).append((char) (absMinutes % 10 + '0'));
  2789                         .append((char) (absMinutes / 10 + '0')).append((char) (absMinutes % 10 + '0'));
  2569                     if (type >= 5 || (type >= 3 && absSeconds > 0)) {
  2790                     output += absMinutes;
       
  2791                     if (type >= 7 || (type >= 5 && absSeconds > 0)) {
  2570                         buf.append((type % 2) == 0 ? ":" : "")
  2792                         buf.append((type % 2) == 0 ? ":" : "")
  2571                             .append((char) (absSeconds / 10 + '0')).append((char) (absSeconds % 10 + '0'));
  2793                             .append((char) (absSeconds / 10 + '0')).append((char) (absSeconds % 10 + '0'));
  2572                     }
  2794                         output += absSeconds;
       
  2795                     }
       
  2796                 }
       
  2797                 if (output == 0) {
       
  2798                     buf.setLength(bufPos);
       
  2799                     buf.append(noOffsetText);
  2573                 }
  2800                 }
  2574             }
  2801             }
  2575             return true;
  2802             return true;
  2576         }
  2803         }
  2577 
  2804 
  2579         public int parse(DateTimeParseContext context, CharSequence text, int position) {
  2806         public int parse(DateTimeParseContext context, CharSequence text, int position) {
  2580             int length = text.length();
  2807             int length = text.length();
  2581             int noOffsetLen = noOffsetText.length();
  2808             int noOffsetLen = noOffsetText.length();
  2582             if (noOffsetLen == 0) {
  2809             if (noOffsetLen == 0) {
  2583                 if (position == length) {
  2810                 if (position == length) {
  2584                     context.setParsedField(OFFSET_SECONDS, 0);
  2811                     return context.setParsedField(OFFSET_SECONDS, 0, position, position);
  2585                     return position;
       
  2586                 }
  2812                 }
  2587             } else {
  2813             } else {
  2588                 if (position == length) {
  2814                 if (position == length) {
  2589                     return ~position;
  2815                     return ~position;
  2590                 }
  2816                 }
  2591                 if (context.subSequenceEquals(text, position, noOffsetText, 0, noOffsetLen)) {
  2817                 if (context.subSequenceEquals(text, position, noOffsetText, 0, noOffsetLen)) {
  2592                     context.setParsedField(OFFSET_SECONDS, 0);
  2818                     return context.setParsedField(OFFSET_SECONDS, 0, position, position + noOffsetLen);
  2593                     return position + noOffsetLen;
       
  2594                 }
  2819                 }
  2595             }
  2820             }
  2596 
  2821 
  2597             // parse normal plus/minus offset
  2822             // parse normal plus/minus offset
  2598             char sign = text.charAt(position);  // IOOBE if invalid position
  2823             char sign = text.charAt(position);  // IOOBE if invalid position
  2599             if (sign == '+' || sign == '-') {
  2824             if (sign == '+' || sign == '-') {
  2600                 // starts
  2825                 // starts
  2601                 int negative = (sign == '-' ? -1 : 1);
  2826                 int negative = (sign == '-' ? -1 : 1);
  2602                 int[] array = new int[4];
  2827                 int[] array = new int[4];
  2603                 array[0] = position + 1;
  2828                 array[0] = position + 1;
  2604                 if (parseNumber(array, 1, text, true) ||
  2829                 if ((parseNumber(array, 1, text, true) ||
  2605                         parseNumber(array, 2, text, type > 0) ||
  2830                         parseNumber(array, 2, text, type >=3) ||
  2606                         parseNumber(array, 3, text, false)) {
  2831                         parseNumber(array, 3, text, false)) == false) {
  2607                     return ~position;
  2832                     // success
  2608                 }
  2833                     long offsetSecs = negative * (array[1] * 3600L + array[2] * 60L + array[3]);
  2609                 long offsetSecs = negative * (array[1] * 3600L + array[2] * 60L + array[3]);
  2834                     return context.setParsedField(OFFSET_SECONDS, offsetSecs, position, array[0]);
  2610                 context.setParsedField(OFFSET_SECONDS, offsetSecs);
  2835                 }
  2611                 return array[0];
  2836             }
  2612             } else {
  2837             // handle special case of empty no offset text
  2613                 // handle special case of empty no offset text
  2838             if (noOffsetLen == 0) {
  2614                 if (noOffsetLen == 0) {
  2839                 return context.setParsedField(OFFSET_SECONDS, 0, position, position + noOffsetLen);
  2615                     context.setParsedField(OFFSET_SECONDS, 0);
  2840             }
  2616                     return position + noOffsetLen;
  2841             return ~position;
  2617                 }
       
  2618                 return ~position;
       
  2619             }
       
  2620         }
  2842         }
  2621 
  2843 
  2622         /**
  2844         /**
  2623          * Parse a two digit zero-prefixed number.
  2845          * Parse a two digit zero-prefixed number.
  2624          *
  2846          *
  2657         }
  2879         }
  2658 
  2880 
  2659         @Override
  2881         @Override
  2660         public String toString() {
  2882         public String toString() {
  2661             String converted = noOffsetText.replace("'", "''");
  2883             String converted = noOffsetText.replace("'", "''");
  2662             return "Offset('" + converted + "'," + PATTERNS[type] + ")";
  2884             return "Offset(" + PATTERNS[type] + ",'" + converted + "')";
  2663         }
  2885         }
  2664     }
  2886     }
  2665 
  2887 
  2666     //-----------------------------------------------------------------------
  2888     //-----------------------------------------------------------------------
  2667     /**
  2889     /**
  2668      * Prints or parses a zone ID.
  2890      * Prints or parses a zone ID.
  2669      */
  2891      */
  2670     static final class ZoneTextPrinterParser implements DateTimePrinterParser {
  2892     static final class ZoneTextPrinterParser extends ZoneIdPrinterParser {
  2671 
  2893 
  2672         /** The text style to output. */
  2894         /** The text style to output. */
  2673         private final TextStyle textStyle;
  2895         private final TextStyle textStyle;
  2674 
  2896 
  2675         ZoneTextPrinterParser(TextStyle textStyle) {
  2897         /** The preferred zoneid map */
       
  2898         private Set<String> preferredZones;
       
  2899 
       
  2900         ZoneTextPrinterParser(TextStyle textStyle, Set<ZoneId> preferredZones) {
       
  2901             super(Queries.zone(), "ZoneText(" + textStyle + ")");
  2676             this.textStyle = Objects.requireNonNull(textStyle, "textStyle");
  2902             this.textStyle = Objects.requireNonNull(textStyle, "textStyle");
       
  2903             if (preferredZones != null && preferredZones.size() != 0) {
       
  2904                 this.preferredZones = new HashSet<>();
       
  2905                 for (ZoneId id : preferredZones) {
       
  2906                     this.preferredZones.add(id.getId());
       
  2907                 }
       
  2908             }
  2677         }
  2909         }
  2678 
  2910 
  2679         private static final int STD = 0;
  2911         private static final int STD = 0;
  2680         private static final int DST = 1;
  2912         private static final int DST = 1;
  2681         private static final int GENERIC = 2;
  2913         private static final int GENERIC = 2;
  2682 
       
  2683         private static final Map<String, SoftReference<Map<Locale, String[]>>> cache =
  2914         private static final Map<String, SoftReference<Map<Locale, String[]>>> cache =
  2684             new ConcurrentHashMap<>();
  2915             new ConcurrentHashMap<>();
  2685 
  2916 
  2686         private static String getDisplayName(String id, int type, TextStyle style, Locale locale) {
  2917         private String getDisplayName(String id, int type, Locale locale) {
  2687             if (style == TextStyle.NARROW) {
  2918             if (textStyle == TextStyle.NARROW) {
  2688                 return null;
  2919                 return null;
  2689             }
  2920             }
  2690             String[] names;
  2921             String[] names;
  2691             SoftReference<Map<Locale, String[]>> ref = cache.get(id);
  2922             SoftReference<Map<Locale, String[]>> ref = cache.get(id);
  2692             Map<Locale, String[]> perLocale;
  2923             Map<Locale, String[]> perLocale = null;
  2693             if (ref == null || (perLocale = ref.get()) == null ||
  2924             if (ref == null || (perLocale = ref.get()) == null ||
  2694                 (names = perLocale.get(locale)) == null) {
  2925                 (names = perLocale.get(locale)) == null) {
  2695                 names = TimeZoneNameUtility.retrieveDisplayNames(id, locale);
  2926                 names = TimeZoneNameUtility.retrieveDisplayNames(id, locale);
  2696                 if (names == null) {
  2927                 if (names == null) {
  2697                     return null;
  2928                     return null;
  2705                 names[6] =
  2936                 names[6] =
  2706                     TimeZoneNameUtility.retrieveGenericDisplayName(id, TimeZone.SHORT,locale);
  2937                     TimeZoneNameUtility.retrieveGenericDisplayName(id, TimeZone.SHORT,locale);
  2707                 if (names[6] == null) {
  2938                 if (names[6] == null) {
  2708                     names[6] = names[0];
  2939                     names[6] = names[0];
  2709                 }
  2940                 }
  2710                 perLocale = new ConcurrentHashMap<>();
  2941                 if (perLocale == null) {
       
  2942                     perLocale = new ConcurrentHashMap<>();
       
  2943                 }
  2711                 perLocale.put(locale, names);
  2944                 perLocale.put(locale, names);
  2712                 ref = new SoftReference<>(perLocale);
  2945                 cache.put(id, new SoftReference<>(perLocale));
  2713                 cache.put(id, ref);
       
  2714             }
  2946             }
  2715             switch (type) {
  2947             switch (type) {
  2716             case STD:
  2948             case STD:
  2717                 return names[style.ordinal() + 1];
  2949                 return names[textStyle.ordinal() + 1];
  2718             case DST:
  2950             case DST:
  2719                 return names[style.ordinal() + 3];
  2951                 return names[textStyle.ordinal() + 3];
  2720             }
  2952             }
  2721             return names[style.ordinal() + 5];
  2953             return names[textStyle.ordinal() + 5];
  2722         }
  2954         }
  2723 
  2955 
  2724         @Override
  2956         @Override
  2725         public boolean print(DateTimePrintContext context, StringBuilder buf) {
  2957         public boolean format(DateTimePrintContext context, StringBuilder buf) {
  2726             ZoneId zone = context.getValue(Queries.zoneId());
  2958             ZoneId zone = context.getValue(Queries.zoneId());
  2727             if (zone == null) {
  2959             if (zone == null) {
  2728                 return false;
  2960                 return false;
  2729             }
  2961             }
  2730             if (zone instanceof ZoneOffset) {
  2962             String zname = zone.getId();
  2731                 buf.append(zone.getId());
  2963             if (!(zone instanceof ZoneOffset)) {
  2732             } else {
       
  2733                 TemporalAccessor dt = context.getTemporal();
  2964                 TemporalAccessor dt = context.getTemporal();
  2734                 Instant instant = null;
  2965                 String name = getDisplayName(zname,
  2735                 if (dt.isSupported(ChronoField.INSTANT_SECONDS)) {
  2966                                              dt.isSupported(ChronoField.INSTANT_SECONDS)
  2736                     instant = Instant.from(dt);
  2967                                              ? (zone.getRules().isDaylightSavings(Instant.from(dt)) ? DST : STD)
  2737                 }
  2968                                              : GENERIC,
  2738                 String name = getDisplayName(zone.getId(),
  2969                                              context.getLocale());
  2739                                              instant == null ? GENERIC
       
  2740                                                              : (zone.getRules().isDaylightSavings(instant) ? DST : STD),
       
  2741                     textStyle, context.getLocale());
       
  2742                 if (name != null) {
  2970                 if (name != null) {
  2743                     buf.append(name);
  2971                     zname = name;
  2744                 } else {
  2972                 }
  2745                     buf.append(zone.getId());
  2973             }
  2746                 }
  2974             buf.append(zname);
  2747             }
       
  2748             return true;
  2975             return true;
  2749         }
  2976         }
  2750 
  2977 
  2751         @Override
  2978         // cache per instance for now
  2752         public int parse(DateTimeParseContext context, CharSequence text, int position) {
  2979         private final Map<Locale, Entry<Integer, SoftReference<PrefixTree>>>
  2753             throw new UnsupportedOperationException();
  2980             cachedTree = new HashMap<>();
  2754         }
  2981         private final Map<Locale, Entry<Integer, SoftReference<PrefixTree>>>
  2755 
  2982             cachedTreeCI = new HashMap<>();
  2756         @Override
  2983 
  2757         public String toString() {
  2984         @Override
  2758             return "ZoneText(" + textStyle + ")";
  2985         protected PrefixTree getTree(DateTimeParseContext context) {
       
  2986             if (textStyle == TextStyle.NARROW) {
       
  2987                 return super.getTree(context);
       
  2988             }
       
  2989             Locale locale = context.getLocale();
       
  2990             boolean isCaseSensitive = context.isCaseSensitive();
       
  2991             Set<String> regionIds = ZoneRulesProvider.getAvailableZoneIds();
       
  2992             int regionIdsSize = regionIds.size();
       
  2993 
       
  2994             Map<Locale, Entry<Integer, SoftReference<PrefixTree>>> cached =
       
  2995                 isCaseSensitive ? cachedTree : cachedTreeCI;
       
  2996 
       
  2997             Entry<Integer, SoftReference<PrefixTree>> entry = null;
       
  2998             PrefixTree tree = null;
       
  2999             String[][] zoneStrings = null;
       
  3000             if ((entry = cached.get(locale)) == null ||
       
  3001                 (entry.getKey() != regionIdsSize ||
       
  3002                 (tree = entry.getValue().get()) == null)) {
       
  3003                 tree = PrefixTree.newTree(context);
       
  3004                 zoneStrings = TimeZoneNameUtility.getZoneStrings(locale);
       
  3005                 for (String[] names : zoneStrings) {
       
  3006                     String zid = names[0];
       
  3007                     if (!regionIds.contains(zid)) {
       
  3008                         continue;
       
  3009                     }
       
  3010                     tree.add(zid, zid);    // don't convert zid -> metazone
       
  3011                     zid = ZoneName.toZid(zid, locale);
       
  3012                     int i = textStyle == TextStyle.FULL ? 1 : 2;
       
  3013                     for (; i < names.length; i += 2) {
       
  3014                         tree.add(names[i], zid);
       
  3015                     }
       
  3016                 }
       
  3017                 // if we have a set of preferred zones, need a copy and
       
  3018                 // add the preferred zones again to overwrite
       
  3019                 if (preferredZones != null) {
       
  3020                     for (String[] names : zoneStrings) {
       
  3021                         String zid = names[0];
       
  3022                         if (!preferredZones.contains(zid) || !regionIds.contains(zid)) {
       
  3023                             continue;
       
  3024                         }
       
  3025                         int i = textStyle == TextStyle.FULL ? 1 : 2;
       
  3026                         for (; i < names.length; i += 2) {
       
  3027                             tree.add(names[i], zid);
       
  3028                        }
       
  3029                     }
       
  3030                 }
       
  3031                 cached.put(locale, new SimpleImmutableEntry<>(regionIdsSize, new SoftReference<>(tree)));
       
  3032             }
       
  3033             return tree;
  2759         }
  3034         }
  2760     }
  3035     }
  2761 
  3036 
  2762     //-----------------------------------------------------------------------
  3037     //-----------------------------------------------------------------------
  2763     /**
  3038     /**
  2764      * Prints or parses a zone ID.
  3039      * Prints or parses a zone ID.
  2765      */
  3040      */
  2766     static final class ZoneIdPrinterParser implements DateTimePrinterParser {
  3041     static class ZoneIdPrinterParser implements DateTimePrinterParser {
  2767         private final TemporalQuery<ZoneId> query;
  3042         private final TemporalQuery<ZoneId> query;
  2768         private final String description;
  3043         private final String description;
  2769 
  3044 
  2770         ZoneIdPrinterParser(TemporalQuery<ZoneId> query, String description) {
  3045         ZoneIdPrinterParser(TemporalQuery<ZoneId> query, String description) {
  2771             this.query = query;
  3046             this.query = query;
  2772             this.description = description;
  3047             this.description = description;
  2773         }
  3048         }
  2774 
  3049 
  2775         //-----------------------------------------------------------------------
  3050         @Override
  2776         @Override
  3051         public boolean format(DateTimePrintContext context, StringBuilder buf) {
  2777         public boolean print(DateTimePrintContext context, StringBuilder buf) {
       
  2778             ZoneId zone = context.getValue(query);
  3052             ZoneId zone = context.getValue(query);
  2779             if (zone == null) {
  3053             if (zone == null) {
  2780                 return false;
  3054                 return false;
  2781             }
  3055             }
  2782             buf.append(zone.getId());
  3056             buf.append(zone.getId());
  2783             return true;
  3057             return true;
  2784         }
  3058         }
  2785 
  3059 
  2786         //-----------------------------------------------------------------------
       
  2787         /**
  3060         /**
  2788          * The cached tree to speed up parsing.
  3061          * The cached tree to speed up parsing.
  2789          */
  3062          */
  2790         private static volatile Entry<Integer, PrefixTree> cachedPrefixTree;
  3063         private static volatile Entry<Integer, PrefixTree> cachedPrefixTree;
  2791         private static volatile Entry<Integer, PrefixTree> cachedPrefixTreeCI;
  3064         private static volatile Entry<Integer, PrefixTree> cachedPrefixTreeCI;
  2792 
  3065 
  2793         /**
  3066         protected PrefixTree getTree(DateTimeParseContext context) {
  2794          * This implementation looks for the longest matching string.
       
  2795          * For example, parsing Etc/GMT-2 will return Etc/GMC-2 rather than just
       
  2796          * Etc/GMC although both are valid.
       
  2797          */
       
  2798         @Override
       
  2799         public int parse(DateTimeParseContext context, CharSequence text, int position) {
       
  2800             int length = text.length();
       
  2801             if (position > length) {
       
  2802                 throw new IndexOutOfBoundsException();
       
  2803             }
       
  2804 
       
  2805             // handle fixed time-zone IDs
       
  2806             if ((text.length() - position) >= 1) {
       
  2807                 char nextChar = text.charAt(position);
       
  2808                 if (nextChar == '+' || nextChar == '-') {
       
  2809                     DateTimeParseContext newContext = context.copy();
       
  2810                     int endPos = OffsetIdPrinterParser.INSTANCE_ID.parse(newContext, text, position);
       
  2811                     if (endPos < 0) {
       
  2812                         return endPos;
       
  2813                     }
       
  2814                     int offset = (int) (long) newContext.getParsed(OFFSET_SECONDS);
       
  2815                     ZoneId zone = ZoneOffset.ofTotalSeconds(offset);
       
  2816                     context.setParsed(zone);
       
  2817                     return endPos;
       
  2818                 }
       
  2819             }
       
  2820 
       
  2821             // prepare parse tree
  3067             // prepare parse tree
  2822             Set<String> regionIds = ZoneRulesProvider.getAvailableZoneIds();
  3068             Set<String> regionIds = ZoneRulesProvider.getAvailableZoneIds();
  2823             final int regionIdsSize = regionIds.size();
  3069             final int regionIdsSize = regionIds.size();
  2824             Entry<Integer, PrefixTree> cached = context.isCaseSensitive()
  3070             Entry<Integer, PrefixTree> cached = context.isCaseSensitive()
  2825                                                 ? cachedPrefixTree : cachedPrefixTreeCI;
  3071                                                 ? cachedPrefixTree : cachedPrefixTreeCI;
  2826             if (cached == null || cached.getKey() != regionIdsSize) {
  3072             if (cached == null || cached.getKey() != regionIdsSize) {
  2827                 synchronized (this) {
  3073                 synchronized (this) {
  2828                     cached = context.isCaseSensitive() ? cachedPrefixTree : cachedPrefixTreeCI;
  3074                     cached = context.isCaseSensitive() ? cachedPrefixTree : cachedPrefixTreeCI;
  2829                     if (cached == null || cached.getKey() != regionIdsSize) {
  3075                     if (cached == null || cached.getKey() != regionIdsSize) {
  2830                         cached = new SimpleImmutableEntry<>(regionIdsSize,
  3076                         cached = new SimpleImmutableEntry<>(regionIdsSize, PrefixTree.newTree(regionIds, context));
  2831                             PrefixTree.newTree(regionIds, context.isCaseSensitive()
       
  2832                                                           ? PrefixTree.STRICT : PrefixTree.CASE_INSENSITIVE));
       
  2833                         if (context.isCaseSensitive()) {
  3077                         if (context.isCaseSensitive()) {
  2834                             cachedPrefixTree = cached;
  3078                             cachedPrefixTree = cached;
  2835                         } else {
  3079                         } else {
  2836                             cachedPrefixTreeCI = cached;
  3080                             cachedPrefixTreeCI = cached;
  2837                         }
  3081                         }
  2838                     }
  3082                     }
  2839                 }
  3083                 }
  2840             }
  3084             }
  2841             PrefixTree tree = cached.getValue();
  3085             return cached.getValue();
       
  3086         }
       
  3087 
       
  3088         /**
       
  3089          * This implementation looks for the longest matching string.
       
  3090          * For example, parsing Etc/GMT-2 will return Etc/GMC-2 rather than just
       
  3091          * Etc/GMC although both are valid.
       
  3092          */
       
  3093         @Override
       
  3094         public int parse(DateTimeParseContext context, CharSequence text, int position) {
       
  3095             int length = text.length();
       
  3096             if (position > length) {
       
  3097                 throw new IndexOutOfBoundsException();
       
  3098             }
       
  3099             if (position == length) {
       
  3100                 return ~position;
       
  3101             }
       
  3102 
       
  3103             // handle fixed time-zone IDs
       
  3104             char nextChar = text.charAt(position);
       
  3105             if (nextChar == '+' || nextChar == '-') {
       
  3106                 return parseOffsetBased(context, text, position, OffsetIdPrinterParser.INSTANCE_ID_Z);
       
  3107             } else if (length >= position + 2) {
       
  3108                 char nextNextChar = text.charAt(position + 1);
       
  3109                 if (context.charEquals(nextChar, 'U') && context.charEquals(nextNextChar, 'T')) {
       
  3110                     if (length >= position + 3 && context.charEquals(text.charAt(position + 2), 'C')) {
       
  3111                         return parseOffsetBased(context, text, position + 3, OffsetIdPrinterParser.INSTANCE_ID_ZERO);
       
  3112                     }
       
  3113                     return parseOffsetBased(context, text, position + 2, OffsetIdPrinterParser.INSTANCE_ID_ZERO);
       
  3114                 } else if (context.charEquals(nextChar, 'G') && length >= position + 3 &&
       
  3115                         context.charEquals(nextNextChar, 'M') && context.charEquals(text.charAt(position + 2), 'T')) {
       
  3116                     return parseOffsetBased(context, text, position + 3, OffsetIdPrinterParser.INSTANCE_ID_ZERO);
       
  3117                 }
       
  3118             }
  2842 
  3119 
  2843             // parse
  3120             // parse
  2844             String parsedZoneId = tree.match(text, position, length);
  3121             PrefixTree tree = getTree(context);
  2845             if (parsedZoneId == null || regionIds.contains(parsedZoneId) == false) {
  3122             ParsePosition ppos = new ParsePosition(position);
  2846                 if (text.charAt(position) == 'Z') {
  3123             String parsedZoneId = tree.match(text, ppos);
       
  3124             if (parsedZoneId == null) {
       
  3125                 if (context.charEquals(nextChar, 'Z')) {
  2847                     context.setParsed(ZoneOffset.UTC);
  3126                     context.setParsed(ZoneOffset.UTC);
  2848                     return position + 1;
  3127                     return position + 1;
  2849                 }
  3128                 }
  2850                 return ~position;
  3129                 return ~position;
  2851             }
  3130             }
  2852             context.setParsed(ZoneId.of(parsedZoneId));
  3131             context.setParsed(ZoneId.of(parsedZoneId));
  2853             return position + parsedZoneId.length();
  3132             return ppos.getIndex();
  2854         }
  3133         }
  2855 
  3134 
       
  3135         private int parseOffsetBased(DateTimeParseContext context, CharSequence text, int position, OffsetIdPrinterParser parser) {
       
  3136             DateTimeParseContext newContext = context.copy();
       
  3137             int endPos = parser.parse(newContext, text, position);
       
  3138             if (endPos < 0) {
       
  3139                 if (parser == OffsetIdPrinterParser.INSTANCE_ID_Z) {
       
  3140                     return ~position;
       
  3141                 }
       
  3142                 context.setParsed(ZoneOffset.UTC);
       
  3143                 return position;
       
  3144             }
       
  3145             int offset = (int) newContext.getParsed(OFFSET_SECONDS).longValue();
       
  3146             ZoneId zone = ZoneOffset.ofTotalSeconds(offset);
       
  3147             context.setParsed(zone);
       
  3148             return endPos;
       
  3149         }
  2856 
  3150 
  2857         @Override
  3151         @Override
  2858         public String toString() {
  3152         public String toString() {
  2859             return description;
  3153             return description;
  2860         }
  3154         }
  2869         protected String value;
  3163         protected String value;
  2870         protected char c0;    // performance optimization to avoid the
  3164         protected char c0;    // performance optimization to avoid the
  2871                               // boundary check cost of key.charat(0)
  3165                               // boundary check cost of key.charat(0)
  2872         protected PrefixTree child;
  3166         protected PrefixTree child;
  2873         protected PrefixTree sibling;
  3167         protected PrefixTree sibling;
  2874 
       
  2875         static final int STRICT = 1;
       
  2876         static final int CASE_INSENSITIVE = 2;
       
  2877         static final int LENIENT = 3;
       
  2878 
  3168 
  2879         private PrefixTree(String k, String v, PrefixTree child) {
  3169         private PrefixTree(String k, String v, PrefixTree child) {
  2880             this.key = k;
  3170             this.key = k;
  2881             this.value = v;
  3171             this.value = v;
  2882             this.child = child;
  3172             this.child = child;
  2886                 c0 = key.charAt(0);
  3176                 c0 = key.charAt(0);
  2887             }
  3177             }
  2888         }
  3178         }
  2889 
  3179 
  2890         /**
  3180         /**
  2891          * Creates a new prefix parsing tree.
  3181          * Creates a new prefix parsing tree based on parse context.
  2892          *
  3182          *
  2893          * @param type  the type of the prefix tree. One of the three supported
  3183          * @param context  the parse context
  2894          *  types, STRICT, CASE_INSENSITIVE and LENIENT
       
  2895          * @return the tree, not null
  3184          * @return the tree, not null
  2896          */
  3185          */
  2897         public static PrefixTree newTree(int type) {
  3186         public static PrefixTree newTree(DateTimeParseContext context) {
  2898             PrefixTree tree;
  3187             //if (!context.isStrict()) {
  2899             switch(type) {
  3188             //    return new LENIENT("", null, null);
  2900                 case STRICT:
  3189             //}
  2901                     tree = new PrefixTree("", null, null);
  3190             if (context.isCaseSensitive()) {
  2902                     break;
  3191                 return new PrefixTree("", null, null);
  2903                 case CASE_INSENSITIVE:
  3192             }
  2904                     tree = new CI("", null, null);
  3193             return new CI("", null, null);
  2905                     break;
       
  2906                 case LENIENT:
       
  2907                     tree = new LENIENT("", null, null);
       
  2908                     break;
       
  2909                 default:
       
  2910                     throw new IllegalArgumentException("Unknown type");
       
  2911             }
       
  2912             return tree;
       
  2913         }
  3194         }
  2914 
  3195 
  2915         /**
  3196         /**
  2916          * Creates a new prefix parsing tree.
  3197          * Creates a new prefix parsing tree.
  2917          *
  3198          *
  2918          * @param keys  a set of strings to build the prefix parsing tree, not null
  3199          * @param keys  a set of strings to build the prefix parsing tree, not null
  2919          * @param type  the type of the prefix tree. One of the three supported
  3200          * @param context  the parse context
  2920          *  types, STRICT, CASE_INSENSITIVE and LENIENT
       
  2921          * @return the tree, not null
  3201          * @return the tree, not null
  2922          */
  3202          */
  2923         public static  PrefixTree newTree(Set<String> keys, int type) {
  3203         public static  PrefixTree newTree(Set<String> keys, DateTimeParseContext context) {
  2924             PrefixTree tree = newTree(type);
  3204             PrefixTree tree = newTree(context);
  2925             for (String k : keys) {
  3205             for (String k : keys) {
  2926                 tree.add0(k, k);
  3206                 tree.add0(k, k);
  2927             }
  3207             }
  2928             return tree;
  3208             return tree;
  2929         }
  3209         }
       
  3210 
       
  3211         /**
       
  3212          * Clone a copy of this tree
       
  3213          */
       
  3214         public PrefixTree copyTree() {
       
  3215             PrefixTree copy = new PrefixTree(key, value, null);
       
  3216             if (child != null) {
       
  3217                 copy.child = child.copyTree();
       
  3218             }
       
  3219             if (sibling != null) {
       
  3220                 copy.sibling = sibling.copyTree();
       
  3221             }
       
  3222             return copy;
       
  3223         }
       
  3224 
  2930 
  3225 
  2931         /**
  3226         /**
  2932          * Adds a pair of {key, value} into the prefix tree.
  3227          * Adds a pair of {key, value} into the prefix tree.
  2933          *
  3228          *
  2934          * @param k  the key, not null
  3229          * @param k  the key, not null
  2956                     c = newNode(subKey, v, null);
  3251                     c = newNode(subKey, v, null);
  2957                     c.sibling = child;
  3252                     c.sibling = child;
  2958                     child = c;
  3253                     child = c;
  2959                     return true;
  3254                     return true;
  2960                 }
  3255                 }
  2961                 // have an existing <key, value> already, keep it.
  3256                 // have an existing <key, value> already, overwrite it
  2962                 if (value != null) {
  3257                 // if (value != null) {
  2963                     return false;
  3258                 //    return false;
  2964                 }
  3259                 //}
  2965                 value = v;
  3260                 value = v;
  2966                 return true;
  3261                 return true;
  2967             }
  3262             }
  2968             // split the existing node
  3263             // split the existing node
  2969             PrefixTree n1 = newNode(key.substring(prefixLen), value, child);
  3264             PrefixTree n1 = newNode(key.substring(prefixLen), value, child);
  3095                 return new CI(k, v, child);
  3390                 return new CI(k, v, child);
  3096             }
  3391             }
  3097 
  3392 
  3098             @Override
  3393             @Override
  3099             protected boolean isEqual(char c1, char c2) {
  3394             protected boolean isEqual(char c1, char c2) {
  3100                 return c1 == c2 ||
  3395                 return DateTimeParseContext.charEqualsIgnoreCase(c1, c2);
  3101                        Character.toUpperCase(c1) == Character.toUpperCase(c2) ||
       
  3102                        Character.toLowerCase(c1) == Character.toLowerCase(c2);
       
  3103             }
  3396             }
  3104 
  3397 
  3105             @Override
  3398             @Override
  3106             protected boolean prefixOf(CharSequence text, int off, int end) {
  3399             protected boolean prefixOf(CharSequence text, int off, int end) {
  3107                 int len = key.length();
  3400                 int len = key.length();
  3211             // validated by caller
  3504             // validated by caller
  3212             this.textStyle = textStyle;
  3505             this.textStyle = textStyle;
  3213         }
  3506         }
  3214 
  3507 
  3215         @Override
  3508         @Override
  3216         public boolean print(DateTimePrintContext context, StringBuilder buf) {
  3509         public boolean format(DateTimePrintContext context, StringBuilder buf) {
  3217             Chrono<?> chrono = context.getValue(Queries.chrono());
  3510             Chronology chrono = context.getValue(Queries.chronology());
  3218             if (chrono == null) {
  3511             if (chrono == null) {
  3219                 return false;
  3512                 return false;
  3220             }
  3513             }
  3221             if (textStyle == null) {
  3514             if (textStyle == null) {
  3222                 buf.append(chrono.getId());
  3515                 buf.append(chrono.getId());
  3226             return true;
  3519             return true;
  3227         }
  3520         }
  3228 
  3521 
  3229         @Override
  3522         @Override
  3230         public int parse(DateTimeParseContext context, CharSequence text, int position) {
  3523         public int parse(DateTimeParseContext context, CharSequence text, int position) {
  3231             return ~position;  // TODO, including case insensitive
  3524             // simple looping parser to find the chronology
       
  3525             if (position < 0 || position > text.length()) {
       
  3526                 throw new IndexOutOfBoundsException();
       
  3527             }
       
  3528             Set<Chronology> chronos = Chronology.getAvailableChronologies();
       
  3529             Chronology bestMatch = null;
       
  3530             int matchLen = -1;
       
  3531             for (Chronology chrono : chronos) {
       
  3532                 String id = chrono.getId();
       
  3533                 int idLen = id.length();
       
  3534                 if (idLen > matchLen && context.subSequenceEquals(text, position, id, 0, idLen)) {
       
  3535                     bestMatch = chrono;
       
  3536                     matchLen = idLen;
       
  3537                 }
       
  3538             }
       
  3539             if (bestMatch == null) {
       
  3540                 return ~position;
       
  3541             }
       
  3542             context.setParsed(bestMatch);
       
  3543             return position + matchLen;
  3232         }
  3544         }
  3233     }
  3545     }
  3234 
  3546 
  3235     //-----------------------------------------------------------------------
  3547     //-----------------------------------------------------------------------
  3236     /**
  3548     /**
  3237      * Prints or parses a localized pattern.
  3549      * Prints or parses a localized pattern.
  3238      */
  3550      */
  3239     static final class LocalizedPrinterParser implements DateTimePrinterParser {
  3551     static final class LocalizedPrinterParser implements DateTimePrinterParser {
  3240         private final FormatStyle dateStyle;
  3552         private final FormatStyle dateStyle;
  3241         private final FormatStyle timeStyle;
  3553         private final FormatStyle timeStyle;
  3242         private final Chrono<?> chrono;
       
  3243 
  3554 
  3244         /**
  3555         /**
  3245          * Constructor.
  3556          * Constructor.
  3246          *
  3557          *
  3247          * @param dateStyle  the date style to use, may be null
  3558          * @param dateStyle  the date style to use, may be null
  3248          * @param timeStyle  the time style to use, may be null
  3559          * @param timeStyle  the time style to use, may be null
  3249          * @param chrono  the chronology to use, not null
       
  3250          */
  3560          */
  3251         LocalizedPrinterParser(FormatStyle dateStyle, FormatStyle timeStyle, Chrono<?> chrono) {
  3561         LocalizedPrinterParser(FormatStyle dateStyle, FormatStyle timeStyle) {
  3252             // validated by caller
  3562             // validated by caller
  3253             this.dateStyle = dateStyle;
  3563             this.dateStyle = dateStyle;
  3254             this.timeStyle = timeStyle;
  3564             this.timeStyle = timeStyle;
  3255             this.chrono = chrono;
  3565         }
  3256         }
  3566 
  3257 
  3567         @Override
  3258         @Override
  3568         public boolean format(DateTimePrintContext context, StringBuilder buf) {
  3259         public boolean print(DateTimePrintContext context, StringBuilder buf) {
  3569             Chronology chrono = Chronology.from(context.getTemporal());
  3260             return formatter(context.getLocale()).toPrinterParser(false).print(context, buf);
  3570             return formatter(context.getLocale(), chrono).toPrinterParser(false).format(context, buf);
  3261         }
  3571         }
  3262 
  3572 
  3263         @Override
  3573         @Override
  3264         public int parse(DateTimeParseContext context, CharSequence text, int position) {
  3574         public int parse(DateTimeParseContext context, CharSequence text, int position) {
  3265             return formatter(context.getLocale()).toPrinterParser(false).parse(context, text, position);
  3575             Chronology chrono = context.getEffectiveChronology();
       
  3576             return formatter(context.getLocale(), chrono).toPrinterParser(false).parse(context, text, position);
  3266         }
  3577         }
  3267 
  3578 
  3268         /**
  3579         /**
  3269          * Gets the formatter to use.
  3580          * Gets the formatter to use.
  3270          *
  3581          *
  3271          * @param locale  the locale to use, not null
  3582          * @param locale  the locale to use, not null
       
  3583          * @param chrono  the chronology to use, not null
  3272          * @return the formatter, not null
  3584          * @return the formatter, not null
  3273          * @throws IllegalArgumentException if the formatter cannot be found
  3585          * @throws IllegalArgumentException if the formatter cannot be found
  3274          */
  3586          */
  3275         private DateTimeFormatter formatter(Locale locale) {
  3587         private DateTimeFormatter formatter(Locale locale, Chronology chrono) {
  3276             return DateTimeFormatStyleProvider.getInstance()
  3588             return DateTimeFormatStyleProvider.getInstance()
  3277                                               .getFormatter(dateStyle, timeStyle, chrono, locale);
  3589                                               .getFormatter(dateStyle, timeStyle, chrono, locale);
  3278         }
  3590         }
  3279 
  3591 
  3280         @Override
  3592         @Override
  3281         public String toString() {
  3593         public String toString() {
  3282             return "Localized(" + (dateStyle != null ? dateStyle : "") + "," +
  3594             return "Localized(" + (dateStyle != null ? dateStyle : "") + "," +
  3283                 (timeStyle != null ? timeStyle : "") + "," + chrono.getId() + ")";
  3595                 (timeStyle != null ? timeStyle : "") + ")";
  3284         }
  3596         }
  3285     }
  3597     }
  3286 
  3598 
  3287 
  3599 
  3288     //-----------------------------------------------------------------------
  3600     //-----------------------------------------------------------------------
  3307             this.chr = chr;
  3619             this.chr = chr;
  3308             this.count = count;
  3620             this.count = count;
  3309         }
  3621         }
  3310 
  3622 
  3311         @Override
  3623         @Override
  3312         public boolean print(DateTimePrintContext context, StringBuilder buf) {
  3624         public boolean format(DateTimePrintContext context, StringBuilder buf) {
  3313             return printerParser(context.getLocale()).print(context, buf);
  3625             return printerParser(context.getLocale()).format(context, buf);
  3314         }
  3626         }
  3315 
  3627 
  3316         @Override
  3628         @Override
  3317         public int parse(DateTimeParseContext context, CharSequence text, int position) {
  3629         public int parse(DateTimeParseContext context, CharSequence text, int position) {
  3318             return printerParser(context.getLocale()).parse(context, text, position);
  3630             return printerParser(context.getLocale()).parse(context, text, position);