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      /**
91       * LONG locale dependent date or time style.
92       */
93      public static final int LONG = DateFormat.LONG;
94      
95      /**
96       * MEDIUM locale dependent date or time style.
97       */
98      public static final int MEDIUM = DateFormat.MEDIUM;
99      
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 }