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