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