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 }