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