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 * LONG locale dependent date or time style.
91 */
92 public static final int LONG = DateFormat.LONG;
93 /**
94 * MEDIUM locale dependent date or time style.
95 */
96 public static final int MEDIUM = DateFormat.MEDIUM;
97 /**
98 * SHORT locale dependent date or time style.
99 */
100 public static final int SHORT = DateFormat.SHORT;
101
102 private static final FormatCache<FastDateFormat> cache= new FormatCache<FastDateFormat>() {
103 @Override
104 protected FastDateFormat createInstance(final String pattern, final TimeZone timeZone, final Locale locale) {
105 return new FastDateFormat(pattern, timeZone, locale);
106 }
107 };
108
109 private final FastDatePrinter printer;
110 private final FastDateParser parser;
111
112 //-----------------------------------------------------------------------
113 /**
114 * <p>Gets a formatter instance using the default pattern in the
115 * default locale.</p>
116 *
117 * @return a date/time formatter
118 */
119 public static FastDateFormat getInstance() {
120 return cache.getInstance();
121 }
122
123 /**
124 * <p>Gets a formatter instance using the specified pattern in the
125 * default locale.</p>
126 *
127 * @param pattern {@link java.text.SimpleDateFormat} compatible
128 * pattern
129 * @return a pattern based date/time formatter
130 * @throws IllegalArgumentException if pattern is invalid
131 */
132 public static FastDateFormat getInstance(final String pattern) {
133 return cache.getInstance(pattern, null, null);
134 }
135
136 /**
137 * <p>Gets a formatter instance using the specified pattern and
138 * time zone.</p>
139 *
140 * @param pattern {@link java.text.SimpleDateFormat} compatible
141 * pattern
142 * @param timeZone optional time zone, overrides time zone of
143 * formatted date
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 TimeZone timeZone) {
148 return cache.getInstance(pattern, timeZone, null);
149 }
150
151 /**
152 * <p>Gets a formatter instance using the specified pattern and
153 * locale.</p>
154 *
155 * @param pattern {@link java.text.SimpleDateFormat} compatible
156 * pattern
157 * @param locale optional locale, overrides system locale
158 * @return a pattern based date/time formatter
159 * @throws IllegalArgumentException if pattern is invalid
160 */
161 public static FastDateFormat getInstance(final String pattern, final Locale locale) {
162 return cache.getInstance(pattern, null, locale);
163 }
164
165 /**
166 * <p>Gets a formatter instance using the specified pattern, time zone
167 * and locale.</p>
168 *
169 * @param pattern {@link java.text.SimpleDateFormat} compatible
170 * pattern
171 * @param timeZone optional time zone, overrides time zone of
172 * formatted date
173 * @param locale optional locale, overrides system locale
174 * @return a pattern based date/time formatter
175 * @throws IllegalArgumentException if pattern is invalid
176 * or {@code null}
177 */
178 public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone, final Locale locale) {
179 return cache.getInstance(pattern, timeZone, locale);
180 }
181
182 //-----------------------------------------------------------------------
183 /**
184 * <p>Gets a date formatter instance using the specified style in the
185 * default time zone and locale.</p>
186 *
187 * @param style date style: FULL, LONG, MEDIUM, or SHORT
188 * @return a localized standard date formatter
189 * @throws IllegalArgumentException if the Locale has no date
190 * pattern defined
191 * @since 2.1
192 */
193 public static FastDateFormat getDateInstance(final int style) {
194 return cache.getDateInstance(style, null, null);
195 }
196
197 /**
198 * <p>Gets a date formatter instance using the specified style and
199 * locale in the default time zone.</p>
200 *
201 * @param style date style: FULL, LONG, MEDIUM, or SHORT
202 * @param locale optional locale, overrides system locale
203 * @return a localized standard date formatter
204 * @throws IllegalArgumentException if the Locale has no date
205 * pattern defined
206 * @since 2.1
207 */
208 public static FastDateFormat getDateInstance(final int style, final Locale locale) {
209 return cache.getDateInstance(style, null, locale);
210 }
211
212 /**
213 * <p>Gets a date formatter instance using the specified style and
214 * time zone in the default locale.</p>
215 *
216 * @param style date style: FULL, LONG, MEDIUM, or SHORT
217 * @param timeZone optional time zone, overrides time zone of
218 * formatted date
219 * @return a localized standard date formatter
220 * @throws IllegalArgumentException if the Locale has no date
221 * pattern defined
222 * @since 2.1
223 */
224 public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone) {
225 return cache.getDateInstance(style, timeZone, null);
226 }
227
228 /**
229 * <p>Gets a date formatter instance using the specified style, time
230 * zone and locale.</p>
231 *
232 * @param style date style: FULL, LONG, MEDIUM, or SHORT
233 * @param timeZone optional time zone, overrides time zone of
234 * formatted date
235 * @param locale optional locale, overrides system locale
236 * @return a localized standard date formatter
237 * @throws IllegalArgumentException if the Locale has no date
238 * pattern defined
239 */
240 public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone, final Locale locale) {
241 return cache.getDateInstance(style, timeZone, locale);
242 }
243
244 //-----------------------------------------------------------------------
245 /**
246 * <p>Gets a time formatter instance using the specified style in the
247 * default time zone and locale.</p>
248 *
249 * @param style time style: FULL, LONG, MEDIUM, or SHORT
250 * @return a localized standard time formatter
251 * @throws IllegalArgumentException if the Locale has no time
252 * pattern defined
253 * @since 2.1
254 */
255 public static FastDateFormat getTimeInstance(final int style) {
256 return cache.getTimeInstance(style, null, null);
257 }
258
259 /**
260 * <p>Gets a time formatter instance using the specified style and
261 * locale in the default time zone.</p>
262 *
263 * @param style time style: FULL, LONG, MEDIUM, or SHORT
264 * @param locale optional locale, overrides system locale
265 * @return a localized standard time formatter
266 * @throws IllegalArgumentException if the Locale has no time
267 * pattern defined
268 * @since 2.1
269 */
270 public static FastDateFormat getTimeInstance(final int style, final Locale locale) {
271 return cache.getTimeInstance(style, null, locale);
272 }
273
274 /**
275 * <p>Gets a time formatter instance using the specified style and
276 * time zone in the default locale.</p>
277 *
278 * @param style time style: FULL, LONG, MEDIUM, or SHORT
279 * @param timeZone optional time zone, overrides time zone of
280 * formatted time
281 * @return a localized standard time formatter
282 * @throws IllegalArgumentException if the Locale has no time
283 * pattern defined
284 * @since 2.1
285 */
286 public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone) {
287 return cache.getTimeInstance(style, timeZone, null);
288 }
289
290 /**
291 * <p>Gets a time formatter instance using the specified style, time
292 * zone and locale.</p>
293 *
294 * @param style time style: FULL, LONG, MEDIUM, or SHORT
295 * @param timeZone optional time zone, overrides time zone of
296 * formatted time
297 * @param locale optional locale, overrides system locale
298 * @return a localized standard time formatter
299 * @throws IllegalArgumentException if the Locale has no time
300 * pattern defined
301 */
302 public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone, final Locale locale) {
303 return cache.getTimeInstance(style, timeZone, locale);
304 }
305
306 //-----------------------------------------------------------------------
307 /**
308 * <p>Gets a date/time formatter instance using the specified style
309 * in the default time zone and locale.</p>
310 *
311 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
312 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
313 * @return a localized standard date/time formatter
314 * @throws IllegalArgumentException if the Locale has no date/time
315 * pattern defined
316 * @since 2.1
317 */
318 public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle) {
319 return cache.getDateTimeInstance(dateStyle, timeStyle, null, null);
320 }
321
322 /**
323 * <p>Gets a date/time formatter instance using the specified style and
324 * locale in the default time zone.</p>
325 *
326 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
327 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
328 * @param locale optional locale, overrides system locale
329 * @return a localized standard date/time formatter
330 * @throws IllegalArgumentException if the Locale has no date/time
331 * pattern defined
332 * @since 2.1
333 */
334 public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final Locale locale) {
335 return cache.getDateTimeInstance(dateStyle, timeStyle, null, locale);
336 }
337
338 /**
339 * <p>Gets a date/time formatter instance using the specified style and
340 * time zone in the default locale.</p>
341 *
342 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
343 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
344 * @param timeZone optional time zone, overrides time zone of
345 * formatted date
346 * @return a localized standard date/time formatter
347 * @throws IllegalArgumentException if the Locale has no date/time
348 * pattern defined
349 * @since 2.1
350 */
351 public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone) {
352 return getDateTimeInstance(dateStyle, timeStyle, timeZone, null);
353 }
354 /**
355 * <p>Gets a date/time formatter instance using the specified style,
356 * time zone and locale.</p>
357 *
358 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
359 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
360 * @param timeZone optional time zone, overrides time zone of
361 * formatted date
362 * @param locale optional locale, overrides system locale
363 * @return a localized standard date/time formatter
364 * @throws IllegalArgumentException if the Locale has no date/time
365 * pattern defined
366 */
367 public static FastDateFormat getDateTimeInstance(
368 final int dateStyle, final int timeStyle, final TimeZone timeZone, final Locale locale) {
369 return cache.getDateTimeInstance(dateStyle, timeStyle, timeZone, locale);
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 * @throws NullPointerException if pattern, timeZone, or locale is null.
381 */
382 protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale) {
383 this(pattern, timeZone, locale, null);
384 }
385
386 // Constructor
387 //-----------------------------------------------------------------------
388 /**
389 * <p>Constructs a new FastDateFormat.</p>
390 *
391 * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
392 * @param timeZone non-null time zone to use
393 * @param locale non-null locale to use
394 * @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
395 * @throws NullPointerException if pattern, timeZone, or locale is null.
396 */
397 protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale, final Date centuryStart) {
398 printer= new FastDatePrinter(pattern, timeZone, locale);
399 parser= new FastDateParser(pattern, timeZone, locale, centuryStart);
400 }
401
402 // Format methods
403 //-----------------------------------------------------------------------
404 /**
405 * <p>Formats a {@code Date}, {@code Calendar} or
406 * {@code Long} (milliseconds) object.</p>
407 * This method is an implementation of {@link Format#format(Object, StringBuilder, FieldPosition)}
408 *
409 * @param obj the object to format
410 * @param toAppendTo the buffer to append to
411 * @param pos the position - ignored
412 * @return the buffer passed in
413 */
414 @Override
415 public StringBuilder format(final Object obj, final StringBuilder toAppendTo, final FieldPosition pos) {
416 return toAppendTo.append(printer.format(obj));
417 }
418
419 /**
420 * <p>Formats a millisecond {@code long} value.</p>
421 *
422 * @param millis the millisecond value to format
423 * @return the formatted string
424 * @since 2.1
425 */
426 @Override
427 public String format(final long millis) {
428 return printer.format(millis);
429 }
430
431 /**
432 * <p>Formats a {@code Date} object using a {@code GregorianCalendar}.</p>
433 *
434 * @param date the date to format
435 * @return the formatted string
436 */
437 @Override
438 public String format(final Date date) {
439 return printer.format(date);
440 }
441
442 /**
443 * <p>Formats a {@code Calendar} object.</p>
444 *
445 * @param calendar the calendar to format
446 * @return the formatted string
447 */
448 @Override
449 public String format(final Calendar calendar) {
450 return printer.format(calendar);
451 }
452
453 /**
454 * <p>Formats a millisecond {@code long} value into the
455 * supplied {@code StringBuffer}.</p>
456 *
457 * @param millis the millisecond value to format
458 * @param buf the buffer to format into
459 * @return the specified string buffer
460 * @since 3.5
461 */
462 @Override
463 public <B extends Appendable> B format(final long millis, final B buf) {
464 return printer.format(millis, buf);
465 }
466
467 /**
468 * <p>Formats a {@code Date} object into the
469 * supplied {@code StringBuffer} using a {@code GregorianCalendar}.</p>
470 *
471 * @param date the date to format
472 * @param buf the buffer to format into
473 * @return the specified string buffer
474 * @since 3.5
475 */
476 @Override
477 public <B extends Appendable> B format(final Date date, final B buf) {
478 return printer.format(date, buf);
479 }
480
481 /**
482 * <p>Formats a {@code Calendar} object into the
483 * supplied {@code StringBuffer}.</p>
484 *
485 * @param calendar the calendar to format
486 * @param buf the buffer to format into
487 * @return the specified string buffer
488 * @since 3.5
489 */
490 @Override
491 public <B extends Appendable> B format(final Calendar calendar, final B buf) {
492 return printer.format(calendar, buf);
493 }
494
495 // Parsing
496 //-----------------------------------------------------------------------
497
498
499 /* (non-Javadoc)
500 * @see DateParser#parse(java.lang.String)
501 */
502 @Override
503 public Date parse(final String source) throws ParseException {
504 return parser.parse(source);
505 }
506
507 /* (non-Javadoc)
508 * @see DateParser#parse(java.lang.String, java.text.ParsePosition)
509 */
510 @Override
511 public Date parse(final String source, final ParsePosition pos) {
512 return parser.parse(source, pos);
513 }
514
515 /*
516 * (non-Javadoc)
517 * @see org.apache.commons.lang3.time.DateParser#parse(java.lang.String, java.text.ParsePosition, java.util.Calendar)
518 */
519 @Override
520 public boolean parse(final String source, final ParsePosition pos, final Calendar calendar) {
521 return parser.parse(source, pos, calendar);
522 }
523
524 /* (non-Javadoc)
525 * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
526 */
527 @Override
528 public Object parseObject(final String source, final ParsePosition pos) {
529 return parser.parseObject(source, pos);
530 }
531
532 // Accessors
533 //-----------------------------------------------------------------------
534 /**
535 * <p>Gets the pattern used by this formatter.</p>
536 *
537 * @return the pattern, {@link java.text.SimpleDateFormat} compatible
538 */
539 @Override
540 public String getPattern() {
541 return printer.getPattern();
542 }
543
544 /**
545 * <p>Gets the time zone used by this formatter.</p>
546 *
547 * <p>This zone is always used for {@code Date} formatting. </p>
548 *
549 * @return the time zone
550 */
551 @Override
552 public TimeZone getTimeZone() {
553 return printer.getTimeZone();
554 }
555
556 /**
557 * <p>Gets the locale used by this formatter.</p>
558 *
559 * @return the locale
560 */
561 @Override
562 public Locale getLocale() {
563 return printer.getLocale();
564 }
565
566 /**
567 * <p>Gets an estimate for the maximum string length that the
568 * formatter will produce.</p>
569 *
570 * <p>The actual formatted length will almost always be less than or
571 * equal to this amount.</p>
572 *
573 * @return the maximum formatted length
574 */
575 public int getMaxLengthEstimate() {
576 return printer.getMaxLengthEstimate();
577 }
578
579 // Basics
580 //-----------------------------------------------------------------------
581 /**
582 * <p>Compares two objects for equality.</p>
583 *
584 * @param obj the object to compare to
585 * @return {@code true} if equal
586 */
587 @Override
588 public boolean equals(final Object obj) {
589 if (obj instanceof FastDateFormat == false) {
590 return false;
591 }
592 final FastDateFormat other = (FastDateFormat) obj;
593 // no need to check parser, as it has same invariants as printer
594 return printer.equals(other.printer);
595 }
596
597 /**
598 * <p>Returns a hash code compatible with equals.</p>
599 *
600 * @return a hash code compatible with equals
601 */
602 @Override
603 public int hashCode() {
604 return printer.hashCode();
605 }
606
607 /**
608 * <p>Gets a debugging string version of this formatter.</p>
609 *
610 * @return a debugging string
611 */
612 @Override
613 public String toString() {
614 return "FastDateFormat[" + printer.getPattern() + "," + printer.getLocale() + "," + printer.getTimeZone().getID() + "]";
615 }
616 }