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