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