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 }