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