View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.util.datetime;
18  
19  import java.text.DateFormat;
20  import java.text.FieldPosition;
21  import java.text.ParseException;
22  import java.text.ParsePosition;
23  import java.util.Calendar;
24  import java.util.Date;
25  import java.util.Locale;
26  import java.util.TimeZone;
27  
28  /**
29   * <p>FastDateFormat is a fast and thread-safe version of
30   * {@link java.text.SimpleDateFormat}.</p>
31   *
32   * <p>To obtain an instance of FastDateFormat, use one of the static factory methods: 
33   * {@link #getInstance(String, TimeZone, Locale)}, {@link #getDateInstance(int, TimeZone, Locale)}, 
34   * {@link #getTimeInstance(int, TimeZone, Locale)}, or {@link #getDateTimeInstance(int, int, TimeZone, Locale)} 
35   * </p>
36   * 
37   * <p>Since FastDateFormat is thread safe, you can use a static member instance:</p>
38   * <code>
39   *   private static final FastDateFormat DATE_FORMATTER = FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.SHORT);
40   * </code>
41   * 
42   * <p>This class can be used as a direct replacement to
43   * {@code SimpleDateFormat} in most formatting and parsing situations.
44   * This class is especially useful in multi-threaded server environments.
45   * {@code SimpleDateFormat} is not thread-safe in any JDK version,
46   * nor will it be as Sun have closed the bug/RFE.
47   * </p>
48   *
49   * <p>All patterns are compatible with
50   * SimpleDateFormat (except time zones and some year patterns - see below).</p>
51   *
52   * <p>Since 3.2, FastDateFormat supports parsing as well as printing.</p>
53   *
54   * <p>Java 1.4 introduced a new pattern letter, {@code 'Z'}, to represent
55   * time zones in RFC822 format (eg. {@code +0800} or {@code -1100}).
56   * This pattern letter can be used here (on all JDK versions).</p>
57   *
58   * <p>In addition, the pattern {@code 'ZZ'} has been made to represent
59   * ISO 8601 extended format time zones (eg. {@code +08:00} or {@code -11:00}).
60   * This introduces a minor incompatibility with Java 1.4, but at a gain of
61   * useful functionality.</p>
62   *
63   * <p>Javadoc cites for the year pattern: <i>For formatting, if the number of
64   * pattern letters is 2, the year is truncated to 2 digits; otherwise it is
65   * interpreted as a number.</i> Starting with Java 1.7 a pattern of 'Y' or
66   * 'YYY' will be formatted as '2003', while it was '03' in former Java
67   * versions. FastDateFormat implements the behavior of Java 7.</p>
68   * 
69   * <p>
70   * Copied and modified from <a href="https://commons.apache.org/proper/commons-lang/">Apache Commons Lang</a>.
71   * </p>
72   * 
73   * @since Apache Commons Lang 2.0
74   */
75  public class FastDateFormat extends Format implements DateParser, DatePrinter {
76      
77      /**
78       * Required for serialization support.
79       *
80       * @see java.io.Serializable
81       */
82      @SuppressWarnings("unused")
83      private static final long serialVersionUID = 2L;
84  
85      /**
86       * FULL locale dependent date or time style.
87       */
88      public static final int FULL = DateFormat.FULL;
89      /**
90       * LONG locale dependent date or time style.
91       */
92      public static final int LONG = DateFormat.LONG;
93      /**
94       * MEDIUM locale dependent date or time style.
95       */
96      public static final int MEDIUM = DateFormat.MEDIUM;
97      /**
98       * SHORT locale dependent date or time style.
99       */
100     public static final int SHORT = DateFormat.SHORT;
101 
102     private static final FormatCache<FastDateFormat> cache= new FormatCache<FastDateFormat>() {
103         @Override
104         protected FastDateFormat createInstance(final String pattern, final TimeZone timeZone, final Locale locale) {
105             return new FastDateFormat(pattern, timeZone, locale);
106         }
107     };
108 
109     private final FastDatePrinter printer;
110     private final FastDateParser parser;
111 
112     //-----------------------------------------------------------------------
113     /**
114      * <p>Gets a formatter instance using the default pattern in the
115      * default locale.</p>
116      *
117      * @return a date/time formatter
118      */
119     public static FastDateFormat getInstance() {
120         return cache.getInstance();
121     }
122 
123     /**
124      * <p>Gets a formatter instance using the specified pattern in the
125      * default locale.</p>
126      *
127      * @param pattern  {@link java.text.SimpleDateFormat} compatible
128      *  pattern
129      * @return a pattern based date/time formatter
130      * @throws IllegalArgumentException if pattern is invalid
131      */
132     public static FastDateFormat getInstance(final String pattern) {
133         return cache.getInstance(pattern, null, null);
134     }
135 
136     /**
137      * <p>Gets a formatter instance using the specified pattern and
138      * time zone.</p>
139      *
140      * @param pattern  {@link java.text.SimpleDateFormat} compatible
141      *  pattern
142      * @param timeZone  optional time zone, overrides time zone of
143      *  formatted date
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 TimeZone timeZone) {
148         return cache.getInstance(pattern, timeZone, null);
149     }
150 
151     /**
152      * <p>Gets a formatter instance using the specified pattern and
153      * locale.</p>
154      *
155      * @param pattern  {@link java.text.SimpleDateFormat} compatible
156      *  pattern
157      * @param locale  optional locale, overrides system locale
158      * @return a pattern based date/time formatter
159      * @throws IllegalArgumentException if pattern is invalid
160      */
161     public static FastDateFormat getInstance(final String pattern, final Locale locale) {
162         return cache.getInstance(pattern, null, locale);
163     }
164 
165     /**
166      * <p>Gets a formatter instance using the specified pattern, time zone
167      * and locale.</p>
168      *
169      * @param pattern  {@link java.text.SimpleDateFormat} compatible
170      *  pattern
171      * @param timeZone  optional time zone, overrides time zone of
172      *  formatted date
173      * @param locale  optional locale, overrides system locale
174      * @return a pattern based date/time formatter
175      * @throws IllegalArgumentException if pattern is invalid
176      *  or {@code null}
177      */
178     public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone, final Locale locale) {
179         return cache.getInstance(pattern, timeZone, locale);
180     }
181 
182     //-----------------------------------------------------------------------
183     /**
184      * <p>Gets a date formatter instance using the specified style in the
185      * default time zone and locale.</p>
186      *
187      * @param style  date style: FULL, LONG, MEDIUM, or SHORT
188      * @return a localized standard date formatter
189      * @throws IllegalArgumentException if the Locale has no date
190      *  pattern defined
191      * @since 2.1
192      */
193     public static FastDateFormat getDateInstance(final int style) {
194         return cache.getDateInstance(style, null, null);
195     }
196 
197     /**
198      * <p>Gets a date formatter instance using the specified style and
199      * locale in the default time zone.</p>
200      *
201      * @param style  date style: FULL, LONG, MEDIUM, or SHORT
202      * @param locale  optional locale, overrides system locale
203      * @return a localized standard date formatter
204      * @throws IllegalArgumentException if the Locale has no date
205      *  pattern defined
206      * @since 2.1
207      */
208     public static FastDateFormat getDateInstance(final int style, final Locale locale) {
209         return cache.getDateInstance(style, null, locale);
210     }
211 
212     /**
213      * <p>Gets a date formatter instance using the specified style and
214      * time zone in the default locale.</p>
215      *
216      * @param style  date style: FULL, LONG, MEDIUM, or SHORT
217      * @param timeZone  optional time zone, overrides time zone of
218      *  formatted date
219      * @return a localized standard date formatter
220      * @throws IllegalArgumentException if the Locale has no date
221      *  pattern defined
222      * @since 2.1
223      */
224     public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone) {
225         return cache.getDateInstance(style, timeZone, null);
226     }
227 
228     /**
229      * <p>Gets a date formatter instance using the specified style, time
230      * zone and locale.</p>
231      *
232      * @param style  date style: FULL, LONG, MEDIUM, or SHORT
233      * @param timeZone  optional time zone, overrides time zone of
234      *  formatted date
235      * @param locale  optional locale, overrides system locale
236      * @return a localized standard date formatter
237      * @throws IllegalArgumentException if the Locale has no date
238      *  pattern defined
239      */
240     public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone, final Locale locale) {
241         return cache.getDateInstance(style, timeZone, locale);
242     }
243 
244     //-----------------------------------------------------------------------
245     /**
246      * <p>Gets a time formatter instance using the specified style in the
247      * default time zone and locale.</p>
248      *
249      * @param style  time style: FULL, LONG, MEDIUM, or SHORT
250      * @return a localized standard time formatter
251      * @throws IllegalArgumentException if the Locale has no time
252      *  pattern defined
253      * @since 2.1
254      */
255     public static FastDateFormat getTimeInstance(final int style) {
256         return cache.getTimeInstance(style, null, null);
257     }
258 
259     /**
260      * <p>Gets a time formatter instance using the specified style and
261      * locale in the default time zone.</p>
262      *
263      * @param style  time style: FULL, LONG, MEDIUM, or SHORT
264      * @param locale  optional locale, overrides system locale
265      * @return a localized standard time formatter
266      * @throws IllegalArgumentException if the Locale has no time
267      *  pattern defined
268      * @since 2.1
269      */
270     public static FastDateFormat getTimeInstance(final int style, final Locale locale) {
271         return cache.getTimeInstance(style, null, locale);
272     }
273 
274     /**
275      * <p>Gets a time formatter instance using the specified style and
276      * time zone in the default locale.</p>
277      *
278      * @param style  time style: FULL, LONG, MEDIUM, or SHORT
279      * @param timeZone  optional time zone, overrides time zone of
280      *  formatted time
281      * @return a localized standard time formatter
282      * @throws IllegalArgumentException if the Locale has no time
283      *  pattern defined
284      * @since 2.1
285      */
286     public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone) {
287         return cache.getTimeInstance(style, timeZone, null);
288     }
289 
290     /**
291      * <p>Gets a time formatter instance using the specified style, time
292      * zone and locale.</p>
293      *
294      * @param style  time style: FULL, LONG, MEDIUM, or SHORT
295      * @param timeZone  optional time zone, overrides time zone of
296      *  formatted time
297      * @param locale  optional locale, overrides system locale
298      * @return a localized standard time formatter
299      * @throws IllegalArgumentException if the Locale has no time
300      *  pattern defined
301      */
302     public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone, final Locale locale) {
303         return cache.getTimeInstance(style, timeZone, locale);
304     }
305 
306     //-----------------------------------------------------------------------
307     /**
308      * <p>Gets a date/time formatter instance using the specified style
309      * in the default time zone and locale.</p>
310      *
311      * @param dateStyle  date style: FULL, LONG, MEDIUM, or SHORT
312      * @param timeStyle  time style: FULL, LONG, MEDIUM, or SHORT
313      * @return a localized standard date/time formatter
314      * @throws IllegalArgumentException if the Locale has no date/time
315      *  pattern defined
316      * @since 2.1
317      */
318     public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle) {
319         return cache.getDateTimeInstance(dateStyle, timeStyle, null, null);
320     }
321 
322     /**
323      * <p>Gets a date/time formatter instance using the specified style and
324      * locale in the default time zone.</p>
325      *
326      * @param dateStyle  date style: FULL, LONG, MEDIUM, or SHORT
327      * @param timeStyle  time style: FULL, LONG, MEDIUM, or SHORT
328      * @param locale  optional locale, overrides system locale
329      * @return a localized standard date/time formatter
330      * @throws IllegalArgumentException if the Locale has no date/time
331      *  pattern defined
332      * @since 2.1
333      */
334     public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final Locale locale) {
335         return cache.getDateTimeInstance(dateStyle, timeStyle, null, locale);
336     }
337 
338     /**
339      * <p>Gets a date/time formatter instance using the specified style and
340      * time zone in the default locale.</p>
341      *
342      * @param dateStyle  date style: FULL, LONG, MEDIUM, or SHORT
343      * @param timeStyle  time style: FULL, LONG, MEDIUM, or SHORT
344      * @param timeZone  optional time zone, overrides time zone of
345      *  formatted date
346      * @return a localized standard date/time formatter
347      * @throws IllegalArgumentException if the Locale has no date/time
348      *  pattern defined
349      * @since 2.1
350      */
351     public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone) {
352         return getDateTimeInstance(dateStyle, timeStyle, timeZone, null);
353     }
354     /**
355      * <p>Gets a date/time formatter instance using the specified style,
356      * time zone and locale.</p>
357      *
358      * @param dateStyle  date style: FULL, LONG, MEDIUM, or SHORT
359      * @param timeStyle  time style: FULL, LONG, MEDIUM, or SHORT
360      * @param timeZone  optional time zone, overrides time zone of
361      *  formatted date
362      * @param locale  optional locale, overrides system locale
363      * @return a localized standard date/time formatter
364      * @throws IllegalArgumentException if the Locale has no date/time
365      *  pattern defined
366      */
367     public static FastDateFormat getDateTimeInstance(
368             final int dateStyle, final int timeStyle, final TimeZone timeZone, final Locale locale) {
369         return cache.getDateTimeInstance(dateStyle, timeStyle, timeZone, locale);
370     }
371 
372     // Constructor
373     //-----------------------------------------------------------------------
374     /**
375      * <p>Constructs a new FastDateFormat.</p>
376      *
377      * @param pattern  {@link java.text.SimpleDateFormat} compatible pattern
378      * @param timeZone  non-null time zone to use
379      * @param locale  non-null locale to use
380      * @throws NullPointerException if pattern, timeZone, or locale is null.
381      */
382     protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale) {
383         this(pattern, timeZone, locale, null);
384     }
385 
386     // Constructor
387     //-----------------------------------------------------------------------
388     /**
389      * <p>Constructs a new FastDateFormat.</p>
390      *
391      * @param pattern  {@link java.text.SimpleDateFormat} compatible pattern
392      * @param timeZone  non-null time zone to use
393      * @param locale  non-null locale to use
394      * @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
395      * @throws NullPointerException if pattern, timeZone, or locale is null.
396      */
397     protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale, final Date centuryStart) {
398         printer= new FastDatePrinter(pattern, timeZone, locale);
399         parser= new FastDateParser(pattern, timeZone, locale, centuryStart);
400     }
401 
402     // Format methods
403     //-----------------------------------------------------------------------
404     /**
405      * <p>Formats a {@code Date}, {@code Calendar} or
406      * {@code Long} (milliseconds) object.</p>
407      * This method is an implementation of {@link Format#format(Object, StringBuilder, FieldPosition)}
408      *
409      * @param obj  the object to format
410      * @param toAppendTo  the buffer to append to
411      * @param pos  the position - ignored
412      * @return the buffer passed in
413      */
414     @Override
415     public StringBuilder format(final Object obj, final StringBuilder toAppendTo, final FieldPosition pos) {
416         return toAppendTo.append(printer.format(obj));
417     }
418 
419     /**
420      * <p>Formats a millisecond {@code long} value.</p>
421      *
422      * @param millis  the millisecond value to format
423      * @return the formatted string
424      * @since 2.1
425      */
426     @Override
427     public String format(final long millis) {
428         return printer.format(millis);
429     }
430 
431     /**
432      * <p>Formats a {@code Date} object using a {@code GregorianCalendar}.</p>
433      *
434      * @param date  the date to format
435      * @return the formatted string
436      */
437     @Override
438     public String format(final Date date) {
439         return printer.format(date);
440     }
441 
442     /**
443      * <p>Formats a {@code Calendar} object.</p>
444      *
445      * @param calendar  the calendar to format
446      * @return the formatted string
447      */
448     @Override
449     public String format(final Calendar calendar) {
450         return printer.format(calendar);
451     }
452 
453     /**
454      * <p>Formats a millisecond {@code long} value into the
455      * supplied {@code StringBuffer}.</p>
456      *
457      * @param millis  the millisecond value to format
458      * @param buf  the buffer to format into
459      * @return the specified string buffer
460      * @since 3.5
461      */
462     @Override
463     public <B extends Appendable> B format(final long millis, final B buf) {
464         return printer.format(millis, buf);
465     }
466 
467     /**
468      * <p>Formats a {@code Date} object into the
469      * supplied {@code StringBuffer} using a {@code GregorianCalendar}.</p>
470      *
471      * @param date  the date to format
472      * @param buf  the buffer to format into
473      * @return the specified string buffer
474      * @since 3.5
475      */
476     @Override
477     public <B extends Appendable> B format(final Date date, final B buf) {
478         return printer.format(date, buf);
479     }
480 
481     /**
482      * <p>Formats a {@code Calendar} object into the
483      * supplied {@code StringBuffer}.</p>
484      *
485      * @param calendar  the calendar to format
486      * @param buf  the buffer to format into
487      * @return the specified string buffer
488      * @since 3.5
489     */
490     @Override
491     public <B extends Appendable> B format(final Calendar calendar, final B buf) {
492         return printer.format(calendar, buf);
493     }
494 
495     // Parsing
496     //-----------------------------------------------------------------------
497 
498 
499     /* (non-Javadoc)
500      * @see DateParser#parse(java.lang.String)
501      */
502     @Override
503     public Date parse(final String source) throws ParseException {
504         return parser.parse(source);
505     }
506 
507     /* (non-Javadoc)
508      * @see DateParser#parse(java.lang.String, java.text.ParsePosition)
509      */
510     @Override
511     public Date parse(final String source, final ParsePosition pos) {
512         return parser.parse(source, pos);
513     }
514 
515     /*
516      * (non-Javadoc)
517      * @see org.apache.commons.lang3.time.DateParser#parse(java.lang.String, java.text.ParsePosition, java.util.Calendar)
518      */
519     @Override
520     public boolean parse(final String source, final ParsePosition pos, final Calendar calendar) {
521         return parser.parse(source, pos, calendar);
522     }
523 
524     /* (non-Javadoc)
525      * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
526      */
527     @Override
528     public Object parseObject(final String source, final ParsePosition pos) {
529         return parser.parseObject(source, pos);
530     }
531 
532     // Accessors
533     //-----------------------------------------------------------------------
534     /**
535      * <p>Gets the pattern used by this formatter.</p>
536      *
537      * @return the pattern, {@link java.text.SimpleDateFormat} compatible
538      */
539     @Override
540     public String getPattern() {
541         return printer.getPattern();
542     }
543 
544     /**
545      * <p>Gets the time zone used by this formatter.</p>
546      *
547      * <p>This zone is always used for {@code Date} formatting. </p>
548      *
549      * @return the time zone
550      */
551     @Override
552     public TimeZone getTimeZone() {
553         return printer.getTimeZone();
554     }
555 
556     /**
557      * <p>Gets the locale used by this formatter.</p>
558      *
559      * @return the locale
560      */
561     @Override
562     public Locale getLocale() {
563         return printer.getLocale();
564     }
565 
566     /**
567      * <p>Gets an estimate for the maximum string length that the
568      * formatter will produce.</p>
569      *
570      * <p>The actual formatted length will almost always be less than or
571      * equal to this amount.</p>
572      *
573      * @return the maximum formatted length
574      */
575     public int getMaxLengthEstimate() {
576         return printer.getMaxLengthEstimate();
577     }
578 
579     // Basics
580     //-----------------------------------------------------------------------
581     /**
582      * <p>Compares two objects for equality.</p>
583      *
584      * @param obj  the object to compare to
585      * @return {@code true} if equal
586      */
587     @Override
588     public boolean equals(final Object obj) {
589         if (obj instanceof FastDateFormat == false) {
590             return false;
591         }
592         final FastDateFormat other = (FastDateFormat) obj;
593         // no need to check parser, as it has same invariants as printer
594         return printer.equals(other.printer);
595     }
596 
597     /**
598      * <p>Returns a hash code compatible with equals.</p>
599      *
600      * @return a hash code compatible with equals
601      */
602     @Override
603     public int hashCode() {
604         return printer.hashCode();
605     }
606 
607     /**
608      * <p>Gets a debugging string version of this formatter.</p>
609      *
610      * @return a debugging string
611      */
612     @Override
613     public String toString() {
614         return "FastDateFormat[" + printer.getPattern() + "," + printer.getLocale() + "," + printer.getTimeZone().getID() + "]";
615     }
616 }