    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      https://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.lang3.time;
    019
    020import java.text.DateFormat;
    021import java.text.FieldPosition;
    022import java.text.Format;
    023import java.text.ParseException;
    024import java.text.ParsePosition;
    025import java.text.SimpleDateFormat;
    026import java.util.Calendar;
    027import java.util.Date;
    028import java.util.GregorianCalendar;
    029import java.util.Locale;
    030import java.util.TimeZone;
    031
    032/**
    033 * FastDateFormat is a fast and thread-safe version of {@link java.text.SimpleDateFormat}.
    034 *
    035 * <p>
    036 * To obtain an instance of FastDateFormat, use one of the static factory methods: {@link #getInstance(String, TimeZone, Locale)},
    037 * {@link #getDateInstance(int, TimeZone, Locale)}, {@link #getTimeInstance(int, TimeZone, Locale)}, or {@link #getDateTimeInstance(int, int, TimeZone, Locale)}
    038 * </p>
    039 *
    040 * <p>
    041 * Since FastDateFormat is thread safe, you can use a static member instance:
    042 * </p>
    043 * {@code
    044 *   private static final FastDateFormat DATE_FORMATTER = FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.SHORT);
    045 * }
    046 *
    047 * <p>
    048 * This class can be used as a direct replacement to {@link SimpleDateFormat} in most formatting and parsing situations. This class is especially useful in
    049 * multi-threaded server environments. {@link SimpleDateFormat} is not thread-safe in any JDK version, nor will it be as Sun have closed the bug/RFE.
    050 * </p>
    051 *
    052 * <p>
    053 * All patterns are compatible with SimpleDateFormat (except time zones and some year patterns - see below).
    054 * </p>
    055 *
    056 * <p>
    057 * Since 3.2, FastDateFormat supports parsing as well as printing.
    058 * </p>
    059 *
    060 * <p>
    061 * Java 1.4 introduced a new pattern letter, {@code 'Z'}, to represent time zones in RFC822 format (for example, {@code +0800} or {@code -1100}). This pattern letter can
    062 * be used here (on all JDK versions).
    063 * </p>
    064 *
    065 * <p>
    066 * In addition, the pattern {@code 'ZZ'} has been made to represent ISO 8601 extended format time zones (for example, {@code +08:00} or {@code -11:00}). This introduces
    067 * a minor incompatibility with Java 1.4, but at a gain of useful functionality.
    068 * </p>
    069 *
    070 * <p>
    071 * Javadoc cites for the year pattern: <i>For formatting, if the number of pattern letters is 2, the year is truncated to 2 digits; otherwise it is interpreted
    072 * as a number.</i> Starting with Java 1.7 a pattern of 'Y' or 'YYY' will be formatted as '2003', while it was '03' in former Java versions. FastDateFormat
    073 * implements the behavior of Java 7.
    074 * </p>
    075 *
    076 * @since 2.0
    077 */
    078public class FastDateFormat extends Format implements DateParser, DatePrinter {
    079
    080    /**
    081     * Required for serialization support.
    082     *
    083     * @see java.io.Serializable
    084     */
    085    private static final long serialVersionUID = 2L;
    086
    087    /**
    088     * FULL locale dependent date or time style.
    089     */
    090    public static final int FULL = DateFormat.FULL;
    091
    092    /**
    093     * LONG locale dependent date or time style.
    094     */
    095    public static final int LONG = DateFormat.LONG;
    096
    097    /**
    098     * MEDIUM locale dependent date or time style.
    099     */
    100    public static final int MEDIUM = DateFormat.MEDIUM;
    101
    102    /**
    103     * SHORT locale dependent date or time style.
    104     */
    105    public static final int SHORT = DateFormat.SHORT;
    106
    107    private static final AbstractFormatCache<FastDateFormat> CACHE = new AbstractFormatCache<FastDateFormat>() {
    108
    109        @Override
    110        protected FastDateFormat createInstance(final String pattern, final TimeZone timeZone, final Locale locale) {
    111            return new FastDateFormat(pattern, timeZone, locale);
    112        }
    113    };
    114
    115    /**
    116     * Clears the cache.
    117     */
    118    static void clear() {
    119        AbstractFormatCache.clear();
    120        CACHE.clearInstance();
    121    }
    122
    123    /**
    124     * Gets a date formatter instance using the specified style in the default time zone and locale.
    125     *
    126     * @param style date style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}.
    127     * @return a localized standard date formatter.
    128     * @throws IllegalArgumentException if the Locale has no date pattern defined.
    129     * @since 2.1
    130     */
    131    public static FastDateFormat getDateInstance(final int style) {
    132        return CACHE.getDateInstance(style, null, null);
    133    }
    134
    135    /**
    136     * Gets a date formatter instance using the specified style and locale in the default time zone.
    137     *
    138     * @param style  date style: {@link #FULL}, LO{@link #FULL},{@link #MEDIUM}, or {@link #SHORT}.
    139     * @param locale optional locale, overrides system locale.
    140     * @return a localized standard date formatter.
    141     * @throws IllegalArgumentException if the Locale has no date pattern defined.
    142     * @since 2.1
    143     */
    144    public static FastDateFormat getDateInstance(final int style, final Locale locale) {
    145        return CACHE.getDateInstance(style, null, locale);
    146    }
    147
    148    /**
    149     * Gets a date formatter instance using the specified style and time zone in the default locale.
    150     *
    151     * @param style    date style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}.
    152     * @param timeZone optional time zone, overrides time zone of formatted date.
    153     * @return a localized standard date formatter.
    154     * @throws IllegalArgumentException if the Locale has no date pattern defined.
    155     * @since 2.1
    156     */
    157    public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone) {
    158        return CACHE.getDateInstance(style, timeZone, null);
    159    }
    160
    161    /**
    162     * Gets a date formatter instance using the specified style, time zone and locale.
    163     *
    164     * @param style    date style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}.
    165     * @param timeZone optional time zone, overrides time zone of formatted date.
    166     * @param locale   optional locale, overrides system locale.
    167     * @return a localized standard date formatter.
    168     * @throws IllegalArgumentException if the Locale has no date pattern defined.
    169     */
    170    public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone, final Locale locale) {
    171        return CACHE.getDateInstance(style, timeZone, locale);
    172    }
    173
    174    /**
    175     * Gets a date/time formatter instance using the specified style in the default time zone and locale.
    176     *
    177     * @param dateStyle date style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}.
    178     * @param timeStyle time style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}.
    179     * @return a localized standard date/time formatter.
    180     * @throws IllegalArgumentException if the Locale has no date/time pattern defined.
    181     * @since 2.1
    182     */
    183    public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle) {
    184        return CACHE.getDateTimeInstance(dateStyle, timeStyle, null, null);
    185    }
    186
    187    /**
    188     * Gets a date/time formatter instance using the specified style and locale in the default time zone.
    189     *
    190     * @param dateStyle date style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}.
    191     * @param timeStyle time style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}.
    192     * @param locale    optional locale, overrides system locale.
    193     * @return a localized standard date/time formatter.
    194     * @throws IllegalArgumentException if the Locale has no date/time pattern defined.
    195     * @since 2.1
    196     */
    197    public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final Locale locale) {
    198        return CACHE.getDateTimeInstance(dateStyle, timeStyle, null, locale);
    199    }
    200
    201    /**
    202     * Gets a date/time formatter instance using the specified style and time zone in the default locale.
    203     *
    204     * @param dateStyle date style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}.
    205     * @param timeStyle time style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}.
    206     * @param timeZone  optional time zone, overrides time zone of formatted date.
    207     * @return a localized standard date/time formatter.
    208     * @throws IllegalArgumentException if the Locale has no date/time pattern defined.
    209     * @since 2.1
    210     */
    211    public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone) {
    212        return getDateTimeInstance(dateStyle, timeStyle, timeZone, null);
    213    }
    214
    215    /**
    216     * Gets a date/time formatter instance using the specified style, time zone and locale.
    217     *
    218     * @param dateStyle date style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}.
    219     * @param timeStyle time style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}.
    220     * @param timeZone  optional time zone, overrides time zone of formatted date.
    221     * @param locale    optional locale, overrides system locale.
    222     * @return a localized standard date/time formatter.
    223     * @throws IllegalArgumentException if the Locale has no date/time pattern defined.
    224     */
    225    public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone, final Locale locale) {
    226        return CACHE.getDateTimeInstance(dateStyle, timeStyle, timeZone, locale);
    227    }
    228
    229    /**
    230     * Gets a formatter instance using the default pattern in the default locale.
    231     *
    232     * @return a date/time formatter.
    233     */
    234    public static FastDateFormat getInstance() {
    235        return CACHE.getInstance();
    236    }
    237
    238    /**
    239     * Gets a formatter instance using the specified pattern in the default locale and time zone.
    240     *
    241     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern.
    242     * @return a pattern based date/time formatter.
    243     * @throws IllegalArgumentException if pattern is invalid.
    244     */
    245    public static FastDateFormat getInstance(final String pattern) {
    246        return CACHE.getInstance(pattern, null, null);
    247    }
    248
    249    /**
    250     * Gets a formatter instance using the specified pattern and locale using the default time zone.
    251     *
    252     * @param pattern {@link java.text.SimpleDateFormat} compatible pattern.
    253     * @param locale  optional locale, overrides system locale.
    254     * @return a pattern based date/time formatter.
    255     * @throws IllegalArgumentException if pattern is invalid.
    256     */
    257    public static FastDateFormat getInstance(final String pattern, final Locale locale) {
    258        return CACHE.getInstance(pattern, null, locale);
    259    }
    260
    261    /**
    262     * Gets a formatter instance using the specified pattern and time zone.
    263     *
    264     * @param pattern  {@link java.text.SimpleDateFormat} compatible pattern.
    265     * @param timeZone optional time zone, overrides time zone of formatted date.
    266     * @return a pattern based date/time formatter.
    267     * @throws IllegalArgumentException if pattern is invalid.
    268     */
    269    public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone) {
    270        return CACHE.getInstance(pattern, timeZone, null);
    271    }
    272
    273    /**
    274     * Gets a formatter instance using the specified pattern, time zone and locale.
    275     *
    276     * @param pattern  {@link java.text.SimpleDateFormat} compatible pattern.
    277     * @param timeZone optional time zone, overrides time zone of formatted date.
    278     * @param locale   optional locale, overrides system locale.
    279     * @return a pattern based date/time formatter.
    280     * @throws IllegalArgumentException if pattern is invalid or {@code null}.
    281     */
    282    public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone, final Locale locale) {
    283        return CACHE.getInstance(pattern, timeZone, locale);
    284    }
    285
    286    /**
    287     * Gets a time formatter instance using the specified style in the default time zone and locale.
    288     *
    289     * @param style time style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}.
    290     * @return a localized standard time formatter.
    291     * @throws IllegalArgumentException if the Locale has no time pattern defined.
    292     * @since 2.1
    293     */
    294    public static FastDateFormat getTimeInstance(final int style) {
    295        return CACHE.getTimeInstance(style, null, null);
    296    }
    297
    298    /**
    299     * Gets a time formatter instance using the specified style and locale in the default time zone.
    300     *
    301     * @param style  time style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}.
    302     * @param locale optional locale, overrides system locale.
    303     * @return a localized standard time formatter.
    304     * @throws IllegalArgumentException if the Locale has no time pattern defined.
    305     * @since 2.1
    306     */
    307    public static FastDateFormat getTimeInstance(final int style, final Locale locale) {
    308        return CACHE.getTimeInstance(style, null, locale);
    309    }
    310
    311    /**
    312     * Gets a time formatter instance using the specified style and time zone in the default locale.
    313     *
    314     * @param style    time style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}.
    315     * @param timeZone optional time zone, overrides time zone of formatted time.
    316     * @return a localized standard time formatter.
    317     * @throws IllegalArgumentException if the Locale has no time pattern defined.
    318     * @since 2.1
    319     */
    320    public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone) {
    321        return CACHE.getTimeInstance(style, timeZone, null);
    322    }
    323
    324    /**
    325     * Gets a time formatter instance using the specified style, time zone and locale.
    326     *
    327     * @param style    time style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}.
    328     * @param timeZone optional time zone, overrides time zone of formatted time.
    329     * @param locale   optional locale, overrides system locale.
    330     * @return a localized standard time formatter.
    331     * @throws IllegalArgumentException if the Locale has no time pattern defined.
    332     */
    333    public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone, final Locale locale) {
    334        return CACHE.getTimeInstance(style, timeZone, locale);
    335    }
    336
    337    /** Our fast printer. */
    338    private final FastDatePrinter printer;
    339    /** Our fast parser. */
    340    private final FastDateParser parser;
    341
    342    // Constructor
    343    /**
    344     * Constructs a new FastDateFormat.
    345     *
    346     * @param pattern  {@link java.text.SimpleDateFormat} compatible pattern.
    347     * @param timeZone non-null time zone to use.
    348     * @param locale   non-null locale to use.
    349     * @throws NullPointerException if pattern, timeZone, or locale is null.
    350     */
    351    protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale) {
    352        this(pattern, timeZone, locale, null);
    353    }
    354
    355    /**
    356     * Constructs a new FastDateFormat.
    357     *
    358     * @param pattern      {@link java.text.SimpleDateFormat} compatible pattern.
    359     * @param timeZone     non-null time zone to use.
    360     * @param locale       non-null locale to use.
    361     * @param centuryStart The start of the 100-year period to use as the "default century" for 2 digit year parsing. If centuryStart is null, defaults to now -
    362     *                     80 years.
    363     * @throws NullPointerException if pattern, timeZone, or locale is null.
    364     */
    365    protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale, final Date centuryStart) {
    366        printer = new FastDatePrinter(pattern, timeZone, locale);
    367        parser = new FastDateParser(pattern, timeZone, locale, centuryStart);
    368    }
    369
    370    /**
    371     * Performs the formatting by applying the rules to the specified calendar.
    372     *
    373     * @param calendar the calendar to format.
    374     * @param buf      the buffer to format into.
    375     * @return the specified string buffer.
    376     * @deprecated Use {@link #format(Calendar, Appendable)}
    377     */
    378    @Deprecated
    379    protected StringBuffer applyRules(final Calendar calendar, final StringBuffer buf) {
    380        return printer.format(calendar, buf);
    381    }
    382
    383    // Basics
    384    /**
    385     * Compares two objects for equality.
    386     *
    387     * @param obj the object to compare to.
    388     * @return {@code true} if equal.
    389     */
    390    @Override
    391    public boolean equals(final Object obj) {
    392        if (!(obj instanceof FastDateFormat)) {
    393            return false;
    394        }
    395        final FastDateFormat other = (FastDateFormat) obj;
    396        // no need to check parser, as it has same invariants as printer
    397        return printer.equals(other.printer);
    398    }
    399
    400    /**
    401     * Formats a {@link Calendar} object.
    402     *
    403     * @param calendar the calendar to format.
    404     * @return the formatted string.
    405     */
    406    @Override
    407    public String format(final Calendar calendar) {
    408        return printer.format(calendar);
    409    }
    410
    411    /**
    412     * Formats a {@link Calendar} object into the supplied {@link StringBuffer}.
    413     *
    414     * @param calendar the calendar to format.
    415     * @param buf      the buffer to format into.
    416     * @return the specified string buffer.
    417     * @since 3.5
    418     */
    419    @Override
    420    public <B extends Appendable> B format(final Calendar calendar, final B buf) {
    421        return printer.format(calendar, buf);
    422    }
    423
    424    /**
    425     * Formats a {@link Calendar} object into the supplied {@link StringBuffer}.
    426     *
    427     * @param calendar the calendar to format.
    428     * @param buf      the buffer to format into.
    429     * @return the specified string buffer.
    430     * @deprecated Use {{@link #format(Calendar, Appendable)}.
    431     */
    432    @Deprecated
    433    @Override
    434    public StringBuffer format(final Calendar calendar, final StringBuffer buf) {
    435        return printer.format(calendar, buf);
    436    }
    437
    438    /**
    439     * Formats a {@link Date} object using a {@link GregorianCalendar}.
    440     *
    441     * @param date the date to format.
    442     * @return the formatted string.
    443     */
    444    @Override
    445    public String format(final Date date) {
    446        return printer.format(date);
    447    }
    448
    449    /**
    450     * Formats a {@link Date} object into the supplied {@link StringBuffer} using a {@link GregorianCalendar}.
    451     *
    452     * @param date the date to format.
    453     * @param buf  the buffer to format into.
    454     * @return the specified string buffer.
    455     * @since 3.5
    456     */
    457    @Override
    458    public <B extends Appendable> B format(final Date date, final B buf) {
    459        return printer.format(date, buf);
    460    }
    461
    462    /**
    463     * Formats a {@link Date} object into the supplied {@link StringBuffer} using a {@link GregorianCalendar}.
    464     *
    465     * @param date the date to format.
    466     * @param buf  the buffer to format into.
    467     * @return the specified string buffer.
    468     * @deprecated Use {{@link #format(Date, Appendable)}.
    469     */
    470    @Deprecated
    471    @Override
    472    public StringBuffer format(final Date date, final StringBuffer buf) {
    473        return printer.format(date, buf);
    474    }
    475
    476    /**
    477     * Formats a millisecond {@code long} value.
    478     *
    479     * @param millis the millisecond value to format.
    480     * @return the formatted string.
    481     * @since 2.1
    482     */
    483    @Override
    484    public String format(final long millis) {
    485        return printer.format(millis);
    486    }
    487
    488    /**
    489     * Formats a millisecond {@code long} value into the supplied {@link StringBuffer}.
    490     *
    491     * @param millis the millisecond value to format.
    492     * @param buf    the buffer to format into.
    493     * @return the specified string buffer.
    494     * @since 3.5
    495     */
    496    @Override
    497    public <B extends Appendable> B format(final long millis, final B buf) {
    498        return printer.format(millis, buf);
    499    }
    500
    501    /**
    502     * Formats a millisecond {@code long} value into the supplied {@link StringBuffer}.
    503     *
    504     * @param millis the millisecond value to format.
    505     * @param buf    the buffer to format into.
    506     * @return the specified string buffer.
    507     * @since 2.1
    508     * @deprecated Use {{@link #format(long, Appendable)}.
    509     */
    510    @Deprecated
    511    @Override
    512    public StringBuffer format(final long millis, final StringBuffer buf) {
    513        return printer.format(millis, buf);
    514    }
    515
    516    /**
    517     * Formats a {@link Date}, {@link Calendar} or {@link Long} (milliseconds) object. This method is an implementation of
    518     * {@link Format#format(Object, StringBuffer, FieldPosition)}
    519     *
    520     * @param obj        the object to format.
    521     * @param toAppendTo the buffer to append to.
    522     * @param pos        the position, ignored.
    523     * @return the given buffer.
    524     */
    525    @Override
    526    public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) {
    527        return toAppendTo.append(printer.format(obj));
    528    }
    529
    530    /**
    531     * Gets the locale used by this formatter.
    532     *
    533     * @return the locale.
    534     */
    535    @Override
    536    public Locale getLocale() {
    537        return printer.getLocale();
    538    }
    539
    540    /**
    541     * Gets an estimate for the maximum string length that the formatter will produce.
    542     *
    543     * <p>
    544     * The actual formatted length will almost always be less than or equal to this amount.
    545     * </p>
    546     *
    547     * @return the maximum formatted length.
    548     */
    549    public int getMaxLengthEstimate() {
    550        return printer.getMaxLengthEstimate();
    551    }
    552
    553    /**
    554     * Gets the pattern used by this formatter.
    555     *
    556     * @return the pattern, {@link java.text.SimpleDateFormat} compatible.
    557     */
    558    @Override
    559    public String getPattern() {
    560        return printer.getPattern();
    561    }
    562
    563    /**
    564     * Gets the time zone used by this formatter.
    565     *
    566     * <p>
    567     * This zone is always used for {@link Date} formatting.
    568     * </p>
    569     *
    570     * @return the time zone.
    571     */
    572    @Override
    573    public TimeZone getTimeZone() {
    574        return printer.getTimeZone();
    575    }
    576
    577    /**
    578     * Returns a hash code compatible with equals.
    579     *
    580     * @return a hash code compatible with equals.
    581     */
    582    @Override
    583    public int hashCode() {
    584        return printer.hashCode();
    585    }
    586
    587    /*
    588     * (non-Javadoc)
    589     *
    590     * @see DateParser#parse(String)
    591     */
    592    @Override
    593    public Date parse(final String source) throws ParseException {
    594        return parser.parse(source);
    595    }
    596
    597    /*
    598     * (non-Javadoc)
    599     *
    600     * @see DateParser#parse(String, java.text.ParsePosition)
    601     */
    602    @Override
    603    public Date parse(final String source, final ParsePosition pos) {
    604        return parser.parse(source, pos);
    605    }
    606
    607    /*
    608     * (non-Javadoc)
    609     *
    610     * @see org.apache.commons.lang3.time.DateParser#parse(String, java.text.ParsePosition, java.util.Calendar)
    611     */
    612    @Override
    613    public boolean parse(final String source, final ParsePosition pos, final Calendar calendar) {
    614        return parser.parse(source, pos, calendar);
    615    }
    616
    617    /*
    618     * (non-Javadoc)
    619     *
    620     * @see java.text.Format#parseObject(String, java.text.ParsePosition)
    621     */
    622    @Override
    623    public Object parseObject(final String source, final ParsePosition pos) {
    624        return parser.parseObject(source, pos);
    625    }
    626
    627    /**
    628     * Gets a debugging string version of this formatter.
    629     *
    630     * @return a debug string.
    631     */
    632    @Override
    633    public String toString() {
    634        return "FastDateFormat[" + printer.getPattern() + "," + printer.getLocale() + "," + printer.getTimeZone().getID() + "]";
    635    }
    636}