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 1436770 2013-01-22 07:09:45Z ggregory $
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.getDateTimeInstance(style, null, 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.getDateTimeInstance(style, null, 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.getDateTimeInstance(style, null, 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.getDateTimeInstance(style, null, 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.getDateTimeInstance(null, 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.getDateTimeInstance(null, 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.getDateTimeInstance(null, 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.getDateTimeInstance(null, 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         printer= new FastDatePrinter(pattern, timeZone, locale);
370         parser= new FastDateParser(pattern, timeZone, locale);
371     }
372 
373     // Format methods
374     //-----------------------------------------------------------------------
375     /**
376      * <p>Formats a {@code Date}, {@code Calendar} or
377      * {@code Long} (milliseconds) object.</p>
378      *
379      * @param obj  the object to format
380      * @param toAppendTo  the buffer to append to
381      * @param pos  the position - ignored
382      * @return the buffer passed in
383      */
384     @Override
385     public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) {
386         return printer.format(obj, toAppendTo, pos);
387     }
388 
389     /**
390      * <p>Formats a millisecond {@code long} value.</p>
391      *
392      * @param millis  the millisecond value to format
393      * @return the formatted string
394      * @since 2.1
395      */
396     @Override
397     public String format(final long millis) {
398         return printer.format(millis);
399     }
400 
401     /**
402      * <p>Formats a {@code Date} object using a {@code GregorianCalendar}.</p>
403      *
404      * @param date  the date to format
405      * @return the formatted string
406      */
407     @Override
408     public String format(final Date date) {
409         return printer.format(date);
410     }
411 
412     /**
413      * <p>Formats a {@code Calendar} object.</p>
414      *
415      * @param calendar  the calendar to format
416      * @return the formatted string
417      */
418     @Override
419     public String format(final Calendar calendar) {
420         return printer.format(calendar);
421     }
422 
423     /**
424      * <p>Formats a milliseond {@code long} value into the
425      * supplied {@code StringBuffer}.</p>
426      *
427      * @param millis  the millisecond value to format
428      * @param buf  the buffer to format into
429      * @return the specified string buffer
430      * @since 2.1
431      */
432     @Override
433     public StringBuffer format(final long millis, final StringBuffer buf) {
434         return printer.format(millis, buf);
435     }
436 
437     /**
438      * <p>Formats a {@code Date} object into the
439      * supplied {@code StringBuffer} using a {@code GregorianCalendar}.</p>
440      *
441      * @param date  the date to format
442      * @param buf  the buffer to format into
443      * @return the specified string buffer
444      */
445     @Override
446     public StringBuffer format(final Date date, final StringBuffer buf) {
447         return printer.format(date, buf);
448     }
449 
450     /**
451      * <p>Formats a {@code Calendar} object into the
452      * supplied {@code StringBuffer}.</p>
453      *
454      * @param calendar  the calendar to format
455      * @param buf  the buffer to format into
456      * @return the specified string buffer
457      */
458     @Override
459     public StringBuffer format(final Calendar calendar, final StringBuffer buf) {
460         return printer.format(calendar, buf);
461     }
462 
463     // Parsing
464     //-----------------------------------------------------------------------
465 
466     
467     /* (non-Javadoc)
468      * @see DateParser#parse(java.lang.String)
469      */
470     @Override
471     public Date parse(final String source) throws ParseException {
472         return parser.parse(source);
473     }
474 
475     /* (non-Javadoc)
476      * @see DateParser#parse(java.lang.String, java.text.ParsePosition)
477      */
478     @Override
479     public Date parse(final String source, final ParsePosition pos) {
480             return parser.parse(source, pos);
481     }
482 
483     /* (non-Javadoc)
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>Gets the pattern used by this formatter.</p>
495      *
496      * @return the pattern, {@link java.text.SimpleDateFormat} compatible
497      */
498     @Override
499     public String getPattern() {
500         return printer.getPattern();
501     }
502 
503     /**
504      * <p>Gets the time zone used by this formatter.</p>
505      *
506      * <p>This zone is always used for {@code Date} formatting. </p>
507      *
508      * @return the time zone
509      */
510     @Override
511     public TimeZone getTimeZone() {
512         return printer.getTimeZone();
513     }
514 
515     /**
516      * <p>Gets the locale used by this formatter.</p>
517      *
518      * @return the locale
519      */
520     @Override
521     public Locale getLocale() {
522         return printer.getLocale();
523     }
524 
525     /**
526      * <p>Gets an estimate for the maximum string length that the
527      * formatter will produce.</p>
528      *
529      * <p>The actual formatted length will almost always be less than or
530      * equal to this amount.</p>
531      *
532      * @return the maximum formatted length
533      */
534     public int getMaxLengthEstimate() {
535         return printer.getMaxLengthEstimate();
536     }
537 
538     // Basics
539     //-----------------------------------------------------------------------
540     /**
541      * <p>Compares two objects for equality.</p>
542      *
543      * @param obj  the object to compare to
544      * @return {@code true} if equal
545      */
546     @Override
547     public boolean equals(final Object obj) {
548         if (obj instanceof FastDateFormat == false) {
549             return false;
550         }
551         final FastDateFormat other = (FastDateFormat) obj;
552         // no need to check parser, as it has same invariants as printer
553         return printer.equals(other.printer);
554     }
555 
556     /**
557      * <p>Returns a hashcode compatible with equals.</p>
558      *
559      * @return a hashcode compatible with equals
560      */
561     @Override
562     public int hashCode() {
563         return printer.hashCode();
564     }
565 
566     /**
567      * <p>Gets a debugging string version of this formatter.</p>
568      *
569      * @return a debugging string
570      */
571     @Override
572     public String toString() {
573         return "FastDateFormat[" + printer.getPattern() + "," + printer.getLocale() + "," + printer.getTimeZone().getID() + "]";
574     }
575 
576 
577     /**
578      * <p>Performs the formatting by applying the rules to the
579      * specified calendar.</p>
580      *
581      * @param calendar  the calendar to format
582      * @param buf  the buffer to format into
583      * @return the specified string buffer
584      */
585     protected StringBuffer applyRules(final Calendar calendar, final StringBuffer buf) {
586         return printer.applyRules(calendar, buf);
587     }
588 
589 
590 }