001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017package org.apache.logging.log4j.core.util.datetime; 018 019import java.text.DateFormat; 020import java.text.FieldPosition; 021import java.text.ParseException; 022import java.text.ParsePosition; 023import java.util.Calendar; 024import java.util.Date; 025import java.util.Locale; 026import java.util.TimeZone; 027 028/** 029 * <p>FastDateFormat is a fast and thread-safe version of 030 * {@link java.text.SimpleDateFormat}.</p> 031 * 032 * <p>To obtain an instance of FastDateFormat, use one of the static factory methods: 033 * {@link #getInstance(String, TimeZone, Locale)}, {@link #getDateInstance(int, TimeZone, Locale)}, 034 * {@link #getTimeInstance(int, TimeZone, Locale)}, or {@link #getDateTimeInstance(int, int, TimeZone, Locale)} 035 * </p> 036 * 037 * <p>Since FastDateFormat is thread safe, you can use a static member instance:</p> 038 * <code> 039 * private static final FastDateFormat DATE_FORMATTER = FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.SHORT); 040 * </code> 041 * 042 * <p>This class can be used as a direct replacement to 043 * {@code SimpleDateFormat} in most formatting and parsing situations. 044 * This class is especially useful in multi-threaded server environments. 045 * {@code SimpleDateFormat} is not thread-safe in any JDK version, 046 * nor will it be as Sun have closed the bug/RFE. 047 * </p> 048 * 049 * <p>All patterns are compatible with 050 * SimpleDateFormat (except time zones and some year patterns - see below).</p> 051 * 052 * <p>Since 3.2, FastDateFormat supports parsing as well as printing.</p> 053 * 054 * <p>Java 1.4 introduced a new pattern letter, {@code 'Z'}, to represent 055 * time zones in RFC822 format (eg. {@code +0800} or {@code -1100}). 056 * This pattern letter can be used here (on all JDK versions).</p> 057 * 058 * <p>In addition, the pattern {@code 'ZZ'} has been made to represent 059 * ISO 8601 extended format time zones (eg. {@code +08:00} or {@code -11:00}). 060 * This introduces a minor incompatibility with Java 1.4, but at a gain of 061 * useful functionality.</p> 062 * 063 * <p>Javadoc cites for the year pattern: <i>For formatting, if the number of 064 * pattern letters is 2, the year is truncated to 2 digits; otherwise it is 065 * interpreted as a number.</i> Starting with Java 1.7 a pattern of 'Y' or 066 * 'YYY' will be formatted as '2003', while it was '03' in former Java 067 * versions. FastDateFormat implements the behavior of Java 7.</p> 068 * 069 * <p> 070 * Copied and modified from <a href="https://commons.apache.org/proper/commons-lang/">Apache Commons Lang</a>. 071 * </p> 072 * 073 * @since Apache Commons Lang 2.0 074 */ 075public class FastDateFormat extends Format implements DateParser, DatePrinter { 076 077 /** 078 * Required for serialization support. 079 * 080 * @see java.io.Serializable 081 */ 082 @SuppressWarnings("unused") 083 private static final long serialVersionUID = 2L; 084 085 /** 086 * FULL locale dependent date or time style. 087 */ 088 public static final int FULL = DateFormat.FULL; 089 090 /** 091 * LONG locale dependent date or time style. 092 */ 093 public static final int LONG = DateFormat.LONG; 094 095 /** 096 * MEDIUM locale dependent date or time style. 097 */ 098 public static final int MEDIUM = DateFormat.MEDIUM; 099 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}