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