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 }