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 * https://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.commons.lang3.time; 018 019import java.text.DateFormat; 020import java.text.FieldPosition; 021import java.text.Format; 022import java.text.ParseException; 023import java.text.ParsePosition; 024import java.text.SimpleDateFormat; 025import java.util.Calendar; 026import java.util.Date; 027import java.util.GregorianCalendar; 028import java.util.Locale; 029import java.util.TimeZone; 030 031/** 032 * FastDateFormat is a fast and thread-safe version of 033 * {@link java.text.SimpleDateFormat}. 034 * 035 * <p>To obtain an instance of FastDateFormat, use one of the static factory methods: 036 * {@link #getInstance(String, TimeZone, Locale)}, {@link #getDateInstance(int, TimeZone, Locale)}, 037 * {@link #getTimeInstance(int, TimeZone, Locale)}, or {@link #getDateTimeInstance(int, int, TimeZone, Locale)} 038 * </p> 039 * 040 * <p>Since FastDateFormat is thread safe, you can use a static member instance:</p> 041 * {@code 042 * private static final FastDateFormat DATE_FORMATTER = FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.SHORT); 043 * } 044 * 045 * <p>This class can be used as a direct replacement to 046 * {@link SimpleDateFormat} in most formatting and parsing situations. 047 * This class is especially useful in multi-threaded server environments. 048 * {@link SimpleDateFormat} is not thread-safe in any JDK version, 049 * nor will it be as Sun have closed the bug/RFE. 050 * </p> 051 * 052 * <p>All patterns are compatible with 053 * SimpleDateFormat (except time zones and some year patterns - see below).</p> 054 * 055 * <p>Since 3.2, FastDateFormat supports parsing as well as printing.</p> 056 * 057 * <p>Java 1.4 introduced a new pattern letter, {@code 'Z'}, to represent 058 * time zones in RFC822 format (eg. {@code +0800} or {@code -1100}). 059 * This pattern letter can be used here (on all JDK versions).</p> 060 * 061 * <p>In addition, the pattern {@code 'ZZ'} has been made to represent 062 * ISO 8601 extended format time zones (eg. {@code +08:00} or {@code -11:00}). 063 * This introduces a minor incompatibility with Java 1.4, but at a gain of 064 * useful functionality.</p> 065 * 066 * <p>Javadoc cites for the year pattern: <i>For formatting, if the number of 067 * pattern letters is 2, the year is truncated to 2 digits; otherwise it is 068 * interpreted as a number.</i> Starting with Java 1.7 a pattern of 'Y' or 069 * 'YYY' will be formatted as '2003', while it was '03' in former Java 070 * versions. FastDateFormat implements the behavior of Java 7.</p> 071 * 072 * @since 2.0 073 */ 074public class FastDateFormat extends Format implements DateParser, DatePrinter { 075 076 /** 077 * Required for serialization support. 078 * 079 * @see java.io.Serializable 080 */ 081 private static final long serialVersionUID = 2L; 082 083 /** 084 * FULL locale dependent date or time style. 085 */ 086 087 public static final int FULL = DateFormat.FULL; 088 089 /** 090 * LONG locale dependent date or time style. 091 */ 092 public static final int LONG = DateFormat.LONG; 093 094 /** 095 * MEDIUM locale dependent date or time style. 096 */ 097 public static final int MEDIUM = DateFormat.MEDIUM; 098 099 /** 100 * SHORT locale dependent date or time style. 101 */ 102 public static final int SHORT = DateFormat.SHORT; 103 104 private static final AbstractFormatCache<FastDateFormat> CACHE = new AbstractFormatCache<FastDateFormat>() { 105 @Override 106 protected FastDateFormat createInstance(final String pattern, final TimeZone timeZone, final Locale locale) { 107 return new FastDateFormat(pattern, timeZone, locale); 108 } 109 }; 110 111 /** 112 * Clears the cache. 113 */ 114 static void clear() { 115 AbstractFormatCache.clear(); 116 CACHE.clearInstance(); 117 } 118 119 /** 120 * Gets a date formatter instance using the specified style in the 121 * default time zone and locale. 122 * 123 * @param style date style: FULL, LONG, MEDIUM, or SHORT 124 * @return a localized standard date formatter 125 * @throws IllegalArgumentException if the Locale has no date 126 * pattern defined 127 * @since 2.1 128 */ 129 public static FastDateFormat getDateInstance(final int style) { 130 return CACHE.getDateInstance(style, null, null); 131 } 132 133 /** 134 * Gets a date formatter instance using the specified style and 135 * locale in the default time zone. 136 * 137 * @param style date style: FULL, LONG, MEDIUM, or SHORT 138 * @param locale optional locale, overrides system locale 139 * @return a localized standard date formatter 140 * @throws IllegalArgumentException if the Locale has no date 141 * pattern defined 142 * @since 2.1 143 */ 144 public static FastDateFormat getDateInstance(final int style, final Locale locale) { 145 return CACHE.getDateInstance(style, null, locale); 146 } 147 148 /** 149 * Gets a date formatter instance using the specified style and 150 * time zone in the default locale. 151 * 152 * @param style date style: FULL, LONG, MEDIUM, or SHORT 153 * @param timeZone optional time zone, overrides time zone of 154 * formatted date 155 * @return a localized standard date formatter 156 * @throws IllegalArgumentException if the Locale has no date 157 * pattern defined 158 * @since 2.1 159 */ 160 public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone) { 161 return CACHE.getDateInstance(style, timeZone, null); 162 } 163 164 /** 165 * Gets a date formatter instance using the specified style, time 166 * zone and locale. 167 * 168 * @param style date style: FULL, LONG, MEDIUM, or SHORT 169 * @param timeZone optional time zone, overrides time zone of 170 * formatted date 171 * @param locale optional locale, overrides system locale 172 * @return a localized standard date formatter 173 * @throws IllegalArgumentException if the Locale has no date 174 * pattern defined 175 */ 176 public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone, final Locale locale) { 177 return CACHE.getDateInstance(style, timeZone, locale); 178 } 179 180 /** 181 * Gets a date/time formatter instance using the specified style 182 * in the default time zone and locale. 183 * 184 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT 185 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT 186 * @return a localized standard date/time formatter 187 * @throws IllegalArgumentException if the Locale has no date/time 188 * pattern defined 189 * @since 2.1 190 */ 191 public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle) { 192 return CACHE.getDateTimeInstance(dateStyle, timeStyle, null, null); 193 } 194 195 /** 196 * Gets a date/time formatter instance using the specified style and 197 * locale in the default time zone. 198 * 199 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT 200 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT 201 * @param locale optional locale, overrides system locale 202 * @return a localized standard date/time formatter 203 * @throws IllegalArgumentException if the Locale has no date/time 204 * pattern defined 205 * @since 2.1 206 */ 207 public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final Locale locale) { 208 return CACHE.getDateTimeInstance(dateStyle, timeStyle, null, locale); 209 } 210 211 /** 212 * Gets a date/time formatter instance using the specified style and 213 * time zone in the default locale. 214 * 215 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT 216 * @param timeStyle time 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/time formatter 220 * @throws IllegalArgumentException if the Locale has no date/time 221 * pattern defined 222 * @since 2.1 223 */ 224 public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone) { 225 return getDateTimeInstance(dateStyle, timeStyle, timeZone, null); 226 } 227 228 /** 229 * Gets a date/time formatter instance using the specified style, 230 * time zone and locale. 231 * 232 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT 233 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT 234 * @param timeZone optional time zone, overrides time zone of 235 * formatted date 236 * @param locale optional locale, overrides system locale 237 * @return a localized standard date/time formatter 238 * @throws IllegalArgumentException if the Locale has no date/time 239 * pattern defined 240 */ 241 public static FastDateFormat getDateTimeInstance( 242 final int dateStyle, final int timeStyle, final TimeZone timeZone, final Locale locale) { 243 return CACHE.getDateTimeInstance(dateStyle, timeStyle, timeZone, locale); 244 } 245 246 /** 247 * Gets a formatter instance using the default pattern in the 248 * default locale. 249 * 250 * @return a date/time formatter 251 */ 252 public static FastDateFormat getInstance() { 253 return CACHE.getInstance(); 254 } 255 256 /** 257 * Gets a formatter instance using the specified pattern in the 258 * default locale. 259 * 260 * @param pattern {@link java.text.SimpleDateFormat} compatible 261 * pattern 262 * @return a pattern based date/time formatter 263 * @throws IllegalArgumentException if pattern is invalid 264 */ 265 public static FastDateFormat getInstance(final String pattern) { 266 return CACHE.getInstance(pattern, null, null); 267 } 268 269 /** 270 * Gets a formatter instance using the specified pattern and 271 * locale. 272 * 273 * @param pattern {@link java.text.SimpleDateFormat} compatible 274 * pattern 275 * @param locale optional locale, overrides system locale 276 * @return a pattern based date/time formatter 277 * @throws IllegalArgumentException if pattern is invalid 278 */ 279 public static FastDateFormat getInstance(final String pattern, final Locale locale) { 280 return CACHE.getInstance(pattern, null, locale); 281 } 282 283 /** 284 * Gets a formatter instance using the specified pattern and 285 * time zone. 286 * 287 * @param pattern {@link java.text.SimpleDateFormat} compatible 288 * pattern 289 * @param timeZone optional time zone, overrides time zone of 290 * formatted date 291 * @return a pattern based date/time formatter 292 * @throws IllegalArgumentException if pattern is invalid 293 */ 294 public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone) { 295 return CACHE.getInstance(pattern, timeZone, null); 296 } 297 298 /** 299 * Gets a formatter instance using the specified pattern, time zone 300 * and locale. 301 * 302 * @param pattern {@link java.text.SimpleDateFormat} compatible 303 * pattern 304 * @param timeZone optional time zone, overrides time zone of 305 * formatted date 306 * @param locale optional locale, overrides system locale 307 * @return a pattern based date/time formatter 308 * @throws IllegalArgumentException if pattern is invalid 309 * or {@code null} 310 */ 311 public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone, final Locale locale) { 312 return CACHE.getInstance(pattern, timeZone, locale); 313 } 314 315 /** 316 * Gets a time formatter instance using the specified style in the 317 * default time zone and locale. 318 * 319 * @param style time style: FULL, LONG, MEDIUM, or SHORT 320 * @return a localized standard time formatter 321 * @throws IllegalArgumentException if the Locale has no time 322 * pattern defined 323 * @since 2.1 324 */ 325 public static FastDateFormat getTimeInstance(final int style) { 326 return CACHE.getTimeInstance(style, null, null); 327 } 328 329 /** 330 * Gets a time formatter instance using the specified style and 331 * locale in the default time zone. 332 * 333 * @param style time style: FULL, LONG, MEDIUM, or SHORT 334 * @param locale optional locale, overrides system locale 335 * @return a localized standard time formatter 336 * @throws IllegalArgumentException if the Locale has no time 337 * pattern defined 338 * @since 2.1 339 */ 340 public static FastDateFormat getTimeInstance(final int style, final Locale locale) { 341 return CACHE.getTimeInstance(style, null, locale); 342 } 343 344 /** 345 * Gets a time formatter instance using the specified style and 346 * time zone in the default locale. 347 * 348 * @param style time style: FULL, LONG, MEDIUM, or SHORT 349 * @param timeZone optional time zone, overrides time zone of 350 * formatted time 351 * @return a localized standard time formatter 352 * @throws IllegalArgumentException if the Locale has no time 353 * pattern defined 354 * @since 2.1 355 */ 356 public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone) { 357 return CACHE.getTimeInstance(style, timeZone, null); 358 } 359 360 /** 361 * Gets a time formatter instance using the specified style, time 362 * zone and locale. 363 * 364 * @param style time style: FULL, LONG, MEDIUM, or SHORT 365 * @param timeZone optional time zone, overrides time zone of 366 * formatted time 367 * @param locale optional locale, overrides system locale 368 * @return a localized standard time formatter 369 * @throws IllegalArgumentException if the Locale has no time 370 * pattern defined 371 */ 372 public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone, final Locale locale) { 373 return CACHE.getTimeInstance(style, timeZone, locale); 374 } 375 376 /** Our fast printer. */ 377 private final FastDatePrinter printer; 378 /** Our fast parser. */ 379 private final FastDateParser parser; 380 381 // Constructor 382 /** 383 * Constructs a new FastDateFormat. 384 * 385 * @param pattern {@link java.text.SimpleDateFormat} compatible pattern 386 * @param timeZone non-null time zone to use 387 * @param locale non-null locale to use 388 * @throws NullPointerException if pattern, timeZone, or locale is null. 389 */ 390 protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale) { 391 this(pattern, timeZone, locale, null); 392 } 393 394 // Constructor 395 /** 396 * Constructs a new FastDateFormat. 397 * 398 * @param pattern {@link java.text.SimpleDateFormat} compatible pattern 399 * @param timeZone non-null time zone to use 400 * @param locale non-null locale to use 401 * @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 402 * @throws NullPointerException if pattern, timeZone, or locale is null. 403 */ 404 protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale, final Date centuryStart) { 405 printer = new FastDatePrinter(pattern, timeZone, locale); 406 parser = new FastDateParser(pattern, timeZone, locale, centuryStart); 407 } 408 409 /** 410 * Performs the formatting by applying the rules to the 411 * specified calendar. 412 * 413 * @param calendar the calendar to format 414 * @param buf the buffer to format into 415 * @return the specified string buffer 416 * @deprecated Use {@link #format(Calendar, Appendable)} 417 */ 418 @Deprecated 419 protected StringBuffer applyRules(final Calendar calendar, final StringBuffer buf) { 420 return printer.applyRules(calendar, buf); 421 } 422 423 // Basics 424 /** 425 * Compares two objects for equality. 426 * 427 * @param obj the object to compare to 428 * @return {@code true} if equal 429 */ 430 @Override 431 public boolean equals(final Object obj) { 432 if (!(obj instanceof FastDateFormat)) { 433 return false; 434 } 435 final FastDateFormat other = (FastDateFormat) obj; 436 // no need to check parser, as it has same invariants as printer 437 return printer.equals(other.printer); 438 } 439 440 /** 441 * Formats a {@link Calendar} object. 442 * 443 * @param calendar the calendar to format 444 * @return the formatted string 445 */ 446 @Override 447 public String format(final Calendar calendar) { 448 return printer.format(calendar); 449 } 450 451 /** 452 * Formats a {@link Calendar} object into the 453 * supplied {@link StringBuffer}. 454 * 455 * @param calendar the calendar to format 456 * @param buf the buffer to format into 457 * @return the specified string buffer 458 * @since 3.5 459 */ 460 @Override 461 public <B extends Appendable> B format(final Calendar calendar, final B buf) { 462 return printer.format(calendar, buf); 463 } 464 465 /** 466 * Formats a {@link Calendar} object into the 467 * supplied {@link StringBuffer}. 468 * 469 * @param calendar the calendar to format 470 * @param buf the buffer to format into 471 * @return the specified string buffer 472 * @deprecated Use {{@link #format(Calendar, Appendable)}. 473 */ 474 @Deprecated 475 @Override 476 public StringBuffer format(final Calendar calendar, final StringBuffer buf) { 477 return printer.format(calendar, buf); 478 } 479 480 /** 481 * Formats a {@link Date} object using a {@link GregorianCalendar}. 482 * 483 * @param date the date to format 484 * @return the formatted string 485 */ 486 @Override 487 public String format(final Date date) { 488 return printer.format(date); 489 } 490 491 /** 492 * Formats a {@link Date} object into the 493 * supplied {@link StringBuffer} using a {@link GregorianCalendar}. 494 * 495 * @param date the date to format 496 * @param buf the buffer to format into 497 * @return the specified string buffer 498 * @since 3.5 499 */ 500 @Override 501 public <B extends Appendable> B format(final Date date, final B buf) { 502 return printer.format(date, buf); 503 } 504 505 /** 506 * Formats a {@link Date} object into the 507 * supplied {@link StringBuffer} using a {@link GregorianCalendar}. 508 * 509 * @param date the date to format 510 * @param buf the buffer to format into 511 * @return the specified string buffer 512 * @deprecated Use {{@link #format(Date, Appendable)}. 513 */ 514 @Deprecated 515 @Override 516 public StringBuffer format(final Date date, final StringBuffer buf) { 517 return printer.format(date, buf); 518 } 519 520 /** 521 * Formats a millisecond {@code long} value. 522 * 523 * @param millis the millisecond value to format 524 * @return the formatted string 525 * @since 2.1 526 */ 527 @Override 528 public String format(final long millis) { 529 return printer.format(millis); 530 } 531 532 /** 533 * Formats a millisecond {@code long} value into the 534 * supplied {@link StringBuffer}. 535 * 536 * @param millis the millisecond value to format 537 * @param buf the buffer to format into 538 * @return the specified string buffer 539 * @since 3.5 540 */ 541 @Override 542 public <B extends Appendable> B format(final long millis, final B buf) { 543 return printer.format(millis, buf); 544 } 545 546 // Parsing 547 548 /** 549 * Formats a millisecond {@code long} value into the 550 * supplied {@link StringBuffer}. 551 * 552 * @param millis the millisecond value to format 553 * @param buf the buffer to format into 554 * @return the specified string buffer 555 * @since 2.1 556 * @deprecated Use {{@link #format(long, Appendable)}. 557 */ 558 @Deprecated 559 @Override 560 public StringBuffer format(final long millis, final StringBuffer buf) { 561 return printer.format(millis, buf); 562 } 563 564 // Format methods 565 /** 566 * Formats a {@link Date}, {@link Calendar} or 567 * {@link Long} (milliseconds) object. 568 * This method is an implementation of {@link Format#format(Object, StringBuffer, FieldPosition)} 569 * 570 * @param obj the object to format 571 * @param toAppendTo the buffer to append to 572 * @param pos the position - ignored 573 * @return the buffer passed in 574 */ 575 @Override 576 public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) { 577 return toAppendTo.append(printer.format(obj)); 578 } 579 580 /** 581 * Gets the locale used by this formatter. 582 * 583 * @return the locale 584 */ 585 @Override 586 public Locale getLocale() { 587 return printer.getLocale(); 588 } 589 590 /** 591 * Gets an estimate for the maximum string length that the 592 * formatter will produce. 593 * 594 * <p>The actual formatted length will almost always be less than or 595 * equal to this amount.</p> 596 * 597 * @return the maximum formatted length 598 */ 599 public int getMaxLengthEstimate() { 600 return printer.getMaxLengthEstimate(); 601 } 602 603 // Accessors 604 /** 605 * Gets the pattern used by this formatter. 606 * 607 * @return the pattern, {@link java.text.SimpleDateFormat} compatible 608 */ 609 @Override 610 public String getPattern() { 611 return printer.getPattern(); 612 } 613 614 /** 615 * Gets the time zone used by this formatter. 616 * 617 * <p>This zone is always used for {@link Date} formatting.</p> 618 * 619 * @return the time zone 620 */ 621 @Override 622 public TimeZone getTimeZone() { 623 return printer.getTimeZone(); 624 } 625 626 /** 627 * Returns a hash code compatible with equals. 628 * 629 * @return a hash code compatible with equals 630 */ 631 @Override 632 public int hashCode() { 633 return printer.hashCode(); 634 } 635 636 /* (non-Javadoc) 637 * @see DateParser#parse(String) 638 */ 639 @Override 640 public Date parse(final String source) throws ParseException { 641 return parser.parse(source); 642 } 643 644 /* (non-Javadoc) 645 * @see DateParser#parse(String, java.text.ParsePosition) 646 */ 647 @Override 648 public Date parse(final String source, final ParsePosition pos) { 649 return parser.parse(source, pos); 650 } 651 652 /* 653 * (non-Javadoc) 654 * @see org.apache.commons.lang3.time.DateParser#parse(String, java.text.ParsePosition, java.util.Calendar) 655 */ 656 @Override 657 public boolean parse(final String source, final ParsePosition pos, final Calendar calendar) { 658 return parser.parse(source, pos, calendar); 659 } 660 661 /* (non-Javadoc) 662 * @see java.text.Format#parseObject(String, java.text.ParsePosition) 663 */ 664 @Override 665 public Object parseObject(final String source, final ParsePosition pos) { 666 return parser.parseObject(source, pos); 667 } 668 669 /** 670 * Gets a debugging string version of this formatter. 671 * 672 * @return a debugging string 673 */ 674 @Override 675 public String toString() { 676 return "FastDateFormat[" + printer.getPattern() + "," + printer.getLocale() + "," + printer.getTimeZone().getID() + "]"; 677 } 678}