Home | History | Annotate | Download | only in text
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one or more
      3  * contributor license agreements.  See the NOTICE file distributed with
      4  * this work for additional information regarding copyright ownership.
      5  * The ASF licenses this file to You under the Apache License, Version 2.0
      6  * (the "License"); you may not use this file except in compliance with
      7  * the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package java.text;
     19 
     20 import java.io.InvalidObjectException;
     21 import java.util.Calendar;
     22 import java.util.Date;
     23 import java.util.Hashtable;
     24 import java.util.Locale;
     25 import java.util.TimeZone;
     26 import libcore.icu.ICU;
     27 import libcore.icu.LocaleData;
     28 
     29 /**
     30  * Formats or parses dates and times.
     31  *
     32  * <p>This class provides factories for obtaining instances configured for a specific locale.
     33  * The most common subclass is {@link SimpleDateFormat}.
     34  *
     35  * <h4>Sample Code</h4>
     36  * <p>This code:
     37  * <pre>
     38  * DateFormat[] formats = new DateFormat[] {
     39  *   DateFormat.getDateInstance(),
     40  *   DateFormat.getDateTimeInstance(),
     41  *   DateFormat.getTimeInstance(),
     42  * };
     43  * for (DateFormat df : formats) {
     44  *   System.out.println(df.format(new Date(0)));
     45  *   df.setTimeZone(TimeZone.getTimeZone("UTC"));
     46  *   System.out.println(df.format(new Date(0)));
     47  * }
     48  * </pre>
     49  *
     50  * <p>Produces this output when run on an {@code en_US} device in the America/Los_Angeles time zone:
     51  * <pre>
     52  * Dec 31, 1969
     53  * Jan 1, 1970
     54  * Dec 31, 1969 4:00:00 PM
     55  * Jan 1, 1970 12:00:00 AM
     56  * 4:00:00 PM
     57  * 12:00:00 AM
     58  * </pre>
     59  * And will produce similarly appropriate localized human-readable output on any user's system.
     60  * Notice how the same point in time when formatted can appear to be a different time when rendered
     61  * for a different time zone. This is one reason why formatting should be left until the data will
     62  * only be presented to a human. Machines should interchange "Unix time" integers.
     63  */
     64 public abstract class DateFormat extends Format {
     65 
     66     private static final long serialVersionUID = 7218322306649953788L;
     67 
     68     /**
     69      * A tri-state boolean. If we're running stand-alone this will be null.
     70      * If we're running in an app, the frameworks will have told us the user preference.
     71      * @hide
     72      */
     73     public static Boolean is24Hour;
     74 
     75     /**
     76      * The calendar that this {@code DateFormat} uses to format a number
     77      * representing a date.
     78      */
     79     protected Calendar calendar;
     80 
     81     /**
     82      * The number format used to format a number.
     83      */
     84     protected NumberFormat numberFormat;
     85 
     86     /**
     87      * The format style constant defining the default format style. The default
     88      * is MEDIUM.
     89      */
     90     public static final int DEFAULT = 2;
     91 
     92     /**
     93      * The format style constant defining the full style.
     94      */
     95     public static final int FULL = 0;
     96 
     97     /**
     98      * The format style constant defining the long style.
     99      */
    100     public static final int LONG = 1;
    101 
    102     /**
    103      * The format style constant defining the medium style.
    104      */
    105     public static final int MEDIUM = 2;
    106 
    107     /**
    108      * The format style constant defining the short style.
    109      */
    110     public static final int SHORT = 3;
    111 
    112     /**
    113      * The {@code FieldPosition} selector for 'G' field alignment, corresponds
    114      * to the {@link Calendar#ERA} field.
    115      */
    116     public static final int ERA_FIELD = 0;
    117 
    118     /**
    119      * The {@code FieldPosition} selector for 'y' field alignment, corresponds
    120      * to the {@link Calendar#YEAR} field.
    121      */
    122     public static final int YEAR_FIELD = 1;
    123 
    124     /**
    125      * The {@code FieldPosition} selector for 'M' field alignment, corresponds
    126      * to the {@link Calendar#MONTH} field.
    127      */
    128     public static final int MONTH_FIELD = 2;
    129 
    130     /**
    131      * The {@code FieldPosition} selector for 'd' field alignment, corresponds
    132      * to the {@link Calendar#DATE} field.
    133      */
    134     public static final int DATE_FIELD = 3;
    135 
    136     /**
    137      * The {@code FieldPosition} selector for 'k' field alignment, corresponds
    138      * to the {@link Calendar#HOUR_OF_DAY} field. {@code HOUR_OF_DAY1_FIELD} is
    139      * used for the one-based 24-hour clock. For example, 23:59 + 01:00 results
    140      * in 24:59.
    141      */
    142     public static final int HOUR_OF_DAY1_FIELD = 4;
    143 
    144     /**
    145      * The {@code FieldPosition} selector for 'H' field alignment, corresponds
    146      * to the {@link Calendar#HOUR_OF_DAY} field. {@code HOUR_OF_DAY0_FIELD} is
    147      * used for the zero-based 24-hour clock. For example, 23:59 + 01:00 results
    148      * in 00:59.
    149      */
    150     public static final int HOUR_OF_DAY0_FIELD = 5;
    151 
    152     /**
    153      * FieldPosition selector for 'm' field alignment, corresponds to the
    154      * {@link Calendar#MINUTE} field.
    155      */
    156     public static final int MINUTE_FIELD = 6;
    157 
    158     /**
    159      * FieldPosition selector for 's' field alignment, corresponds to the
    160      * {@link Calendar#SECOND} field.
    161      */
    162     public static final int SECOND_FIELD = 7;
    163 
    164     /**
    165      * FieldPosition selector for 'S' field alignment, corresponds to the
    166      * {@link Calendar#MILLISECOND} field.
    167      */
    168     public static final int MILLISECOND_FIELD = 8;
    169 
    170     /**
    171      * FieldPosition selector for 'E' field alignment, corresponds to the
    172      * {@link Calendar#DAY_OF_WEEK} field.
    173      */
    174     public static final int DAY_OF_WEEK_FIELD = 9;
    175 
    176     /**
    177      * FieldPosition selector for 'D' field alignment, corresponds to the
    178      * {@link Calendar#DAY_OF_YEAR} field.
    179      */
    180     public static final int DAY_OF_YEAR_FIELD = 10;
    181 
    182     /**
    183      * FieldPosition selector for 'F' field alignment, corresponds to the
    184      * {@link Calendar#DAY_OF_WEEK_IN_MONTH} field.
    185      */
    186     public static final int DAY_OF_WEEK_IN_MONTH_FIELD = 11;
    187 
    188     /**
    189      * FieldPosition selector for 'w' field alignment, corresponds to the
    190      * {@link Calendar#WEEK_OF_YEAR} field.
    191      */
    192     public static final int WEEK_OF_YEAR_FIELD = 12;
    193 
    194     /**
    195      * FieldPosition selector for 'W' field alignment, corresponds to the
    196      * {@link Calendar#WEEK_OF_MONTH} field.
    197      */
    198     public static final int WEEK_OF_MONTH_FIELD = 13;
    199 
    200     /**
    201      * FieldPosition selector for 'a' field alignment, corresponds to the
    202      * {@link Calendar#AM_PM} field.
    203      */
    204     public static final int AM_PM_FIELD = 14;
    205 
    206     /**
    207      * FieldPosition selector for 'h' field alignment, corresponding to the
    208      * {@link Calendar#HOUR} field.
    209      */
    210     public static final int HOUR1_FIELD = 15;
    211 
    212     /**
    213      * The {@code FieldPosition} selector for 'K' field alignment, corresponding to the
    214      * {@link Calendar#HOUR} field.
    215      */
    216     public static final int HOUR0_FIELD = 16;
    217 
    218     /**
    219      * The {@code FieldPosition} selector for 'z' field alignment, corresponds
    220      * to the {@link Calendar#ZONE_OFFSET} and {@link Calendar#DST_OFFSET}
    221      * fields.
    222      */
    223     public static final int TIMEZONE_FIELD = 17;
    224 
    225     /**
    226      * Constructs a new instance of {@code DateFormat}.
    227      */
    228     protected DateFormat() {
    229     }
    230 
    231     /**
    232      * Returns a new instance of {@code DateFormat} with the same properties.
    233      */
    234     @Override
    235     public Object clone() {
    236         DateFormat clone = (DateFormat) super.clone();
    237         clone.calendar = (Calendar) calendar.clone();
    238         clone.numberFormat = (NumberFormat) numberFormat.clone();
    239         return clone;
    240     }
    241 
    242     /**
    243      * Compares this date format with the specified object and indicates if they
    244      * are equal.
    245      *
    246      * @param object
    247      *            the object to compare with this date format.
    248      * @return {@code true} if {@code object} is a {@code DateFormat} object and
    249      *         it has the same properties as this date format; {@code false}
    250      *         otherwise.
    251      * @see #hashCode
    252      */
    253     @Override
    254     public boolean equals(Object object) {
    255         if (this == object) {
    256             return true;
    257         }
    258         if (!(object instanceof DateFormat)) {
    259             return false;
    260         }
    261         DateFormat dateFormat = (DateFormat) object;
    262         return numberFormat.equals(dateFormat.numberFormat)
    263                 && calendar.getTimeZone().equals(
    264                         dateFormat.calendar.getTimeZone())
    265                 && calendar.getFirstDayOfWeek() == dateFormat.calendar
    266                         .getFirstDayOfWeek()
    267                 && calendar.getMinimalDaysInFirstWeek() == dateFormat.calendar
    268                         .getMinimalDaysInFirstWeek()
    269                 && calendar.isLenient() == dateFormat.calendar.isLenient();
    270     }
    271 
    272     /**
    273      * Formats the specified object as a string using the pattern of this date
    274      * format and appends the string to the specified string buffer.
    275      * <p>
    276      * If the {@code field} member of {@code field} contains a value specifying
    277      * a format field, then its {@code beginIndex} and {@code endIndex} members
    278      * will be updated with the position of the first occurrence of this field
    279      * in the formatted text.
    280      *
    281      * @param object
    282      *            the source object to format, must be a {@code Date} or a
    283      *            {@code Number}. If {@code object} is a number then a date is
    284      *            constructed using the {@code longValue()} of the number.
    285      * @param buffer
    286      *            the target string buffer to append the formatted date/time to.
    287      * @param field
    288      *            on input: an optional alignment field; on output: the offsets
    289      *            of the alignment field in the formatted text.
    290      * @return the string buffer.
    291      * @throws IllegalArgumentException
    292      *            if {@code object} is neither a {@code Date} nor a
    293      *            {@code Number} instance.
    294      */
    295     @Override
    296     public final StringBuffer format(Object object, StringBuffer buffer, FieldPosition field) {
    297         if (object instanceof Date) {
    298             return format((Date) object, buffer, field);
    299         }
    300         if (object instanceof Number) {
    301             return format(new Date(((Number) object).longValue()), buffer, field);
    302         }
    303         throw new IllegalArgumentException("Bad class: " + object.getClass());
    304     }
    305 
    306     /**
    307      * Formats the specified date using the rules of this date format.
    308      *
    309      * @param date
    310      *            the date to format.
    311      * @return the formatted string.
    312      */
    313     public final String format(Date date) {
    314         return format(date, new StringBuffer(), new FieldPosition(0)).toString();
    315     }
    316 
    317     /**
    318      * Formats the specified date as a string using the pattern of this date
    319      * format and appends the string to the specified string buffer.
    320      * <p>
    321      * If the {@code field} member of {@code field} contains a value specifying
    322      * a format field, then its {@code beginIndex} and {@code endIndex} members
    323      * will be updated with the position of the first occurrence of this field
    324      * in the formatted text.
    325      *
    326      * @param date
    327      *            the date to format.
    328      * @param buffer
    329      *            the target string buffer to append the formatted date/time to.
    330      * @param field
    331      *            on input: an optional alignment field; on output: the offsets
    332      *            of the alignment field in the formatted text.
    333      * @return the string buffer.
    334      */
    335     public abstract StringBuffer format(Date date, StringBuffer buffer, FieldPosition field);
    336 
    337     /**
    338      * Returns an array of locales for which custom {@code DateFormat} instances
    339      * are available.
    340      * <p>Note that Android does not support user-supplied locale service providers.
    341      */
    342     public static Locale[] getAvailableLocales() {
    343         return ICU.getAvailableDateFormatLocales();
    344     }
    345 
    346     /**
    347      * Returns the calendar used by this {@code DateFormat}.
    348      *
    349      * @return the calendar used by this date format.
    350      */
    351     public Calendar getCalendar() {
    352         return calendar;
    353     }
    354 
    355     /**
    356      * Returns a {@code DateFormat} instance for formatting and parsing dates in
    357      * the DEFAULT style for the default locale.
    358      *
    359      * @return the {@code DateFormat} instance for the default style and locale.
    360      */
    361     public static final DateFormat getDateInstance() {
    362         return getDateInstance(DEFAULT);
    363     }
    364 
    365     /**
    366      * Returns a {@code DateFormat} instance for formatting and parsing dates in
    367      * the specified style for the user's default locale.
    368      * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
    369      * @param style
    370      *            one of SHORT, MEDIUM, LONG, FULL, or DEFAULT.
    371      * @return the {@code DateFormat} instance for {@code style} and the default
    372      *         locale.
    373      * @throws IllegalArgumentException
    374      *             if {@code style} is not one of SHORT, MEDIUM, LONG, FULL, or
    375      *             DEFAULT.
    376      */
    377     public static final DateFormat getDateInstance(int style) {
    378         checkDateStyle(style);
    379         return getDateInstance(style, Locale.getDefault());
    380     }
    381 
    382     /**
    383      * Returns a {@code DateFormat} instance for formatting and parsing dates in
    384      * the specified style for the specified locale.
    385      *
    386      * @param style
    387      *            one of SHORT, MEDIUM, LONG, FULL, or DEFAULT.
    388      * @param locale
    389      *            the locale.
    390      * @throws IllegalArgumentException
    391      *             if {@code style} is not one of SHORT, MEDIUM, LONG, FULL, or
    392      *             DEFAULT.
    393      * @return the {@code DateFormat} instance for {@code style} and
    394      *         {@code locale}.
    395      */
    396     public static final DateFormat getDateInstance(int style, Locale locale) {
    397         checkDateStyle(style);
    398         if (locale == null) {
    399             throw new NullPointerException("locale == null");
    400         }
    401         return new SimpleDateFormat(LocaleData.get(locale).getDateFormat(style), locale);
    402     }
    403 
    404     /**
    405      * Returns a {@code DateFormat} instance for formatting and parsing dates
    406      * and time values in the DEFAULT style for the default locale.
    407      *
    408      * @return the {@code DateFormat} instance for the default style and locale.
    409      */
    410     public static final DateFormat getDateTimeInstance() {
    411         return getDateTimeInstance(DEFAULT, DEFAULT);
    412     }
    413 
    414     /**
    415      * Returns a {@code DateFormat} instance for formatting and parsing of both
    416      * dates and time values in the manner appropriate for the user's default locale.
    417      * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
    418      * @param dateStyle
    419      *            one of SHORT, MEDIUM, LONG, FULL, or DEFAULT.
    420      * @param timeStyle
    421      *            one of SHORT, MEDIUM, LONG, FULL, or DEFAULT.
    422      * @return the {@code DateFormat} instance for {@code dateStyle},
    423      *         {@code timeStyle} and the default locale.
    424      * @throws IllegalArgumentException
    425      *             if {@code dateStyle} or {@code timeStyle} is not one of
    426      *             SHORT, MEDIUM, LONG, FULL, or DEFAULT.
    427      */
    428     public static final DateFormat getDateTimeInstance(int dateStyle, int timeStyle) {
    429         checkTimeStyle(timeStyle);
    430         checkDateStyle(dateStyle);
    431         return getDateTimeInstance(dateStyle, timeStyle, Locale.getDefault());
    432     }
    433 
    434     /**
    435      * Returns a {@code DateFormat} instance for formatting and parsing dates
    436      * and time values in the specified styles for the specified locale.
    437      *
    438      * @param dateStyle
    439      *            one of SHORT, MEDIUM, LONG, FULL, or DEFAULT.
    440      * @param timeStyle
    441      *            one of SHORT, MEDIUM, LONG, FULL, or DEFAULT.
    442      * @param locale
    443      *            the locale.
    444      * @return the {@code DateFormat} instance for {@code dateStyle},
    445      *         {@code timeStyle} and {@code locale}.
    446      * @throws IllegalArgumentException
    447      *             if {@code dateStyle} or {@code timeStyle} is not one of
    448      *             SHORT, MEDIUM, LONG, FULL, or DEFAULT.
    449      */
    450     public static final DateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale locale) {
    451         checkTimeStyle(timeStyle);
    452         checkDateStyle(dateStyle);
    453         if (locale == null) {
    454             throw new NullPointerException("locale == null");
    455         }
    456         LocaleData localeData = LocaleData.get(locale);
    457         String pattern = localeData.getDateFormat(dateStyle) + " " + localeData.getTimeFormat(timeStyle);
    458         return new SimpleDateFormat(pattern, locale);
    459     }
    460 
    461     /**
    462      * Returns a {@code DateFormat} instance for formatting and parsing dates
    463      * and times in the SHORT style for the default locale.
    464      *
    465      * @return the {@code DateFormat} instance for the SHORT style and default
    466      *         locale.
    467      */
    468     public static final DateFormat getInstance() {
    469         return getDateTimeInstance(SHORT, SHORT);
    470     }
    471 
    472     /**
    473      * @hide for internal use only.
    474      */
    475     public static final void set24HourTimePref(boolean is24Hour) {
    476         DateFormat.is24Hour = is24Hour;
    477     }
    478 
    479     /**
    480      * Returns the {@code NumberFormat} used by this {@code DateFormat}.
    481      *
    482      * @return the {@code NumberFormat} used by this date format.
    483      */
    484     public NumberFormat getNumberFormat() {
    485         return numberFormat;
    486     }
    487 
    488     /**
    489      * Returns a {@code DateFormat} instance for formatting and parsing time
    490      * values in the DEFAULT style for the default locale.
    491      *
    492      * @return the {@code DateFormat} instance for the default style and locale.
    493      */
    494     public static final DateFormat getTimeInstance() {
    495         return getTimeInstance(DEFAULT);
    496     }
    497 
    498     /**
    499      * Returns a {@code DateFormat} instance for formatting and parsing time
    500      * values in the specified style for the user's default locale.
    501      * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
    502      * @param style
    503      *            one of SHORT, MEDIUM, LONG, FULL, or DEFAULT.
    504      * @return the {@code DateFormat} instance for {@code style} and the default
    505      *         locale.
    506      * @throws IllegalArgumentException
    507      *             if {@code style} is not one of SHORT, MEDIUM, LONG, FULL, or
    508      *             DEFAULT.
    509      */
    510     public static final DateFormat getTimeInstance(int style) {
    511         checkTimeStyle(style);
    512         return getTimeInstance(style, Locale.getDefault());
    513     }
    514 
    515     /**
    516      * Returns a {@code DateFormat} instance for formatting and parsing time
    517      * values in the specified style for the specified locale.
    518      *
    519      * @param style
    520      *            one of SHORT, MEDIUM, LONG, FULL, or DEFAULT.
    521      * @param locale
    522      *            the locale.
    523      * @throws IllegalArgumentException
    524      *             if {@code style} is not one of SHORT, MEDIUM, LONG, FULL, or
    525      *             DEFAULT.
    526      * @return the {@code DateFormat} instance for {@code style} and
    527      *         {@code locale}.
    528      */
    529     public static final DateFormat getTimeInstance(int style, Locale locale) {
    530         checkTimeStyle(style);
    531         if (locale == null) {
    532             throw new NullPointerException("locale == null");
    533         }
    534 
    535         return new SimpleDateFormat(LocaleData.get(locale).getTimeFormat(style), locale);
    536     }
    537 
    538     /**
    539      * Returns the time zone of this date format's calendar.
    540      *
    541      * @return the time zone of the calendar used by this date format.
    542      */
    543     public TimeZone getTimeZone() {
    544         return calendar.getTimeZone();
    545     }
    546 
    547     @Override
    548     public int hashCode() {
    549         return calendar.getFirstDayOfWeek()
    550                 + calendar.getMinimalDaysInFirstWeek()
    551                 + calendar.getTimeZone().hashCode()
    552                 + (calendar.isLenient() ? 1231 : 1237)
    553                 + numberFormat.hashCode();
    554     }
    555 
    556     /**
    557      * Indicates whether the calendar used by this date format is lenient.
    558      *
    559      * @return {@code true} if the calendar is lenient; {@code false} otherwise.
    560      */
    561     public boolean isLenient() {
    562         return calendar.isLenient();
    563     }
    564 
    565     /**
    566      * Parses a date from the specified string using the rules of this date
    567      * format.
    568      *
    569      * @param string
    570      *            the string to parse.
    571      * @return the {@code Date} resulting from the parsing.
    572      * @throws ParseException
    573      *         if an error occurs during parsing.
    574      */
    575     public Date parse(String string) throws ParseException {
    576         ParsePosition position = new ParsePosition(0);
    577         Date date = parse(string, position);
    578         if (position.getIndex() == 0) {
    579             throw new ParseException("Unparseable date: \"" + string + "\"",
    580                     position.getErrorIndex());
    581         }
    582         return date;
    583     }
    584 
    585     /**
    586      * Parses a date from the specified string starting at the index specified
    587      * by {@code position}. If the string is successfully parsed then the index
    588      * of the {@code ParsePosition} is updated to the index following the parsed
    589      * text. On error, the index is unchanged and the error index of {@code
    590      * ParsePosition} is set to the index where the error occurred.
    591      * <p>
    592      * By default, parsing is lenient: If the input is not in the form used by
    593      * this object's format method but can still be parsed as a date, then the
    594      * parse succeeds. Clients may insist on strict adherence to the format by
    595      * calling {@code setLenient(false)}.
    596      *
    597      * @param string
    598      *            the string to parse.
    599      * @param position
    600      *            input/output parameter, specifies the start index in {@code
    601      *            string} from where to start parsing. If parsing is successful,
    602      *            it is updated with the index following the parsed text; on
    603      *            error, the index is unchanged and the error index is set to
    604      *            the index where the error occurred.
    605      * @return the date resulting from the parse, or {@code null} if there is an
    606      *         error.
    607      */
    608     public abstract Date parse(String string, ParsePosition position);
    609 
    610     /**
    611      * Parses a date from the specified string starting at the index specified
    612      * by {@code position}. If the string is successfully parsed then the index
    613      * of the {@code ParsePosition} is updated to the index following the parsed
    614      * text. On error, the index is unchanged and the error index of
    615      * {@code ParsePosition} is set to the index where the error occurred.
    616      * <p>
    617      * By default, parsing is lenient: If the input is not in the form used by
    618      * this object's format method but can still be parsed as a date, then the
    619      * parse succeeds. Clients may insist on strict adherence to the format by
    620      * calling {@code setLenient(false)}.
    621      *
    622      * @param string
    623      *            the string to parse.
    624      * @param position
    625      *            input/output parameter, specifies the start index in
    626      *            {@code string} from where to start parsing. If parsing is
    627      *            successful, it is updated with the index following the parsed
    628      *            text; on error, the index is unchanged and the error index
    629      *            is set to the index where the error occurred.
    630      * @return the date resulting from the parsing, or {@code null} if there is
    631      *         an error.
    632      */
    633     @Override
    634     public Object parseObject(String string, ParsePosition position) {
    635         return parse(string, position);
    636     }
    637 
    638     /**
    639      * Sets the calendar used by this date format.
    640      *
    641      * @param cal
    642      *            the new calendar.
    643      */
    644     public void setCalendar(Calendar cal) {
    645         calendar = cal;
    646     }
    647 
    648     /**
    649      * Specifies whether or not date/time parsing shall be lenient. With lenient
    650      * parsing, the parser may use heuristics to interpret inputs that do not
    651      * precisely match this object's format. With strict parsing, inputs must
    652      * match this object's format.
    653      *
    654      * @param value
    655      *            {@code true} to set the calendar to be lenient, {@code false}
    656      *            otherwise.
    657      */
    658     public void setLenient(boolean value) {
    659         calendar.setLenient(value);
    660     }
    661 
    662     /**
    663      * Sets the {@code NumberFormat} used by this date format.
    664      *
    665      * @param format
    666      *            the new number format.
    667      */
    668     public void setNumberFormat(NumberFormat format) {
    669         numberFormat = format;
    670     }
    671 
    672     /**
    673      * Sets the time zone of the calendar used by this date format.
    674      *
    675      * @param timezone
    676      *            the new time zone.
    677      */
    678     public void setTimeZone(TimeZone timezone) {
    679         calendar.setTimeZone(timezone);
    680     }
    681 
    682     /**
    683      * The instances of this inner class are used as attribute keys and values
    684      * in {@code AttributedCharacterIterator} that the
    685      * {@link SimpleDateFormat#formatToCharacterIterator(Object)} method returns.
    686      * <p>
    687      * There is no public constructor in this class, the only instances are the
    688      * constants defined here.
    689      */
    690     public static class Field extends Format.Field {
    691 
    692         private static final long serialVersionUID = 7441350119349544720L;
    693 
    694         private static Hashtable<Integer, Field> table = new Hashtable<Integer, Field>();
    695 
    696         /**
    697          * Marks the era part of a date.
    698          */
    699         public static final Field ERA = new Field("era", Calendar.ERA);
    700 
    701         /**
    702          * Marks the year part of a date.
    703          */
    704         public static final Field YEAR = new Field("year", Calendar.YEAR);
    705 
    706         /**
    707          * Marks the month part of a date.
    708          */
    709         public static final Field MONTH = new Field("month", Calendar.MONTH);
    710 
    711         /**
    712          * Marks the hour of the day part of a date (0-11).
    713          */
    714         public static final Field HOUR_OF_DAY0 = new Field("hour of day", Calendar.HOUR_OF_DAY);
    715 
    716         /**
    717          * Marks the hour of the day part of a date (1-12).
    718          */
    719         public static final Field HOUR_OF_DAY1 = new Field("hour of day 1", -1);
    720 
    721         /**
    722          * Marks the minute part of a time.
    723          */
    724         public static final Field MINUTE = new Field("minute", Calendar.MINUTE);
    725 
    726         /**
    727          * Marks the second part of a time.
    728          */
    729         public static final Field SECOND = new Field("second", Calendar.SECOND);
    730 
    731         /**
    732          * Marks the millisecond part of a time.
    733          */
    734         public static final Field MILLISECOND = new Field("millisecond", Calendar.MILLISECOND);
    735 
    736         /**
    737          * Marks the day of the week part of a date.
    738          */
    739         public static final Field DAY_OF_WEEK = new Field("day of week", Calendar.DAY_OF_WEEK);
    740 
    741         /**
    742          * Marks the day of the month part of a date.
    743          */
    744         public static final Field DAY_OF_MONTH = new Field("day of month", Calendar.DAY_OF_MONTH);
    745 
    746         /**
    747          * Marks the day of the year part of a date.
    748          */
    749         public static final Field DAY_OF_YEAR = new Field("day of year", Calendar.DAY_OF_YEAR);
    750 
    751         /**
    752          * Marks the day of the week in the month part of a date.
    753          */
    754         public static final Field DAY_OF_WEEK_IN_MONTH = new Field("day of week in month",
    755                 Calendar.DAY_OF_WEEK_IN_MONTH);
    756 
    757         /**
    758          * Marks the week of the year part of a date.
    759          */
    760         public static final Field WEEK_OF_YEAR = new Field("week of year",
    761                 Calendar.WEEK_OF_YEAR);
    762 
    763         /**
    764          * Marks the week of the month part of a date.
    765          */
    766         public static final Field WEEK_OF_MONTH = new Field("week of month",
    767                 Calendar.WEEK_OF_MONTH);
    768 
    769         /**
    770          * Marks the time indicator part of a date.
    771          */
    772         public static final Field AM_PM = new Field("am pm", Calendar.AM_PM);
    773 
    774         /**
    775          * Marks the hour part of a date (0-11).
    776          */
    777         public static final Field HOUR0 = new Field("hour", Calendar.HOUR);
    778 
    779         /**
    780          * Marks the hour part of a date (1-12).
    781          */
    782         public static final Field HOUR1 = new Field("hour 1", -1);
    783 
    784         /**
    785          * Marks the time zone part of a date.
    786          */
    787         public static final Field TIME_ZONE = new Field("time zone", -1);
    788 
    789         /**
    790          * The calendar field that this field represents.
    791          */
    792         private int calendarField = -1;
    793 
    794         /**
    795          * Constructs a new instance of {@code DateFormat.Field} with the given
    796          * fieldName and calendar field.
    797          *
    798          * @param fieldName
    799          *            the field name.
    800          * @param calendarField
    801          *            the calendar field type of the field.
    802          */
    803         protected Field(String fieldName, int calendarField) {
    804             super(fieldName);
    805             this.calendarField = calendarField;
    806             if (calendarField != -1 && table.get(Integer.valueOf(calendarField)) == null) {
    807                 table.put(Integer.valueOf(calendarField), this);
    808             }
    809         }
    810 
    811         /**
    812          * Returns the Calendar field that this field represents.
    813          *
    814          * @return the calendar field.
    815          */
    816         public int getCalendarField() {
    817             return calendarField;
    818         }
    819 
    820         /**
    821          * Returns the {@code DateFormat.Field} instance for the given calendar
    822          * field.
    823          *
    824          * @param calendarField
    825          *            a calendar field constant.
    826          * @return the {@code DateFormat.Field} corresponding to
    827          *         {@code calendarField}.
    828          * @throws IllegalArgumentException
    829          *             if {@code calendarField} is negative or greater than the
    830          *             field count of {@code Calendar}.
    831          */
    832         public static Field ofCalendarField(int calendarField) {
    833             if (calendarField < 0 || calendarField >= Calendar.FIELD_COUNT) {
    834                 throw new IllegalArgumentException("Field out of range: " + calendarField);
    835             }
    836             return table.get(Integer.valueOf(calendarField));
    837         }
    838     }
    839 
    840     private static void checkDateStyle(int style) {
    841         if (!(style == SHORT || style == MEDIUM || style == LONG
    842                 || style == FULL || style == DEFAULT)) {
    843             throw new IllegalArgumentException("Illegal date style: " + style);
    844         }
    845     }
    846 
    847     private static void checkTimeStyle(int style) {
    848         if (!(style == SHORT || style == MEDIUM || style == LONG
    849                 || style == FULL || style == DEFAULT)) {
    850             throw new IllegalArgumentException("Illegal time style: " + style);
    851         }
    852     }
    853 }
    854