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