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.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 * </code> 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 FormatCache<FastDateFormat> cache = new FormatCache<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 /** Our fast printer. */ 112 private final FastDatePrinter printer; 113 114 /** Our fast parser. */ 115 private final FastDateParser parser; 116 117 /** 118 * Gets a formatter instance using the default pattern in the 119 * default locale. 120 * 121 * @return a date/time formatter 122 */ 123 public static FastDateFormat getInstance() { 124 return cache.getInstance(); 125 } 126 127 /** 128 * Gets a formatter instance using the specified pattern in the 129 * default locale. 130 * 131 * @param pattern {@link java.text.SimpleDateFormat} compatible 132 * pattern 133 * @return a pattern based date/time formatter 134 * @throws IllegalArgumentException if pattern is invalid 135 */ 136 public static FastDateFormat getInstance(final String pattern) { 137 return cache.getInstance(pattern, null, null); 138 } 139 140 /** 141 * Gets a formatter instance using the specified pattern and 142 * time zone. 143 * 144 * @param pattern {@link java.text.SimpleDateFormat} compatible 145 * pattern 146 * @param timeZone optional time zone, overrides time zone of 147 * formatted date 148 * @return a pattern based date/time formatter 149 * @throws IllegalArgumentException if pattern is invalid 150 */ 151 public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone) { 152 return cache.getInstance(pattern, timeZone, null); 153 } 154 155 /** 156 * Gets a formatter instance using the specified pattern and 157 * locale. 158 * 159 * @param pattern {@link java.text.SimpleDateFormat} compatible 160 * pattern 161 * @param locale optional locale, overrides system locale 162 * @return a pattern based date/time formatter 163 * @throws IllegalArgumentException if pattern is invalid 164 */ 165 public static FastDateFormat getInstance(final String pattern, final Locale locale) { 166 return cache.getInstance(pattern, null, locale); 167 } 168 169 /** 170 * Gets a formatter instance using the specified pattern, time zone 171 * and locale. 172 * 173 * @param pattern {@link java.text.SimpleDateFormat} compatible 174 * pattern 175 * @param timeZone optional time zone, overrides time zone of 176 * formatted date 177 * @param locale optional locale, overrides system locale 178 * @return a pattern based date/time formatter 179 * @throws IllegalArgumentException if pattern is invalid 180 * or {@code null} 181 */ 182 public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone, final Locale locale) { 183 return cache.getInstance(pattern, timeZone, locale); 184 } 185 186 /** 187 * Gets a date formatter instance using the specified style in the 188 * default time zone and locale. 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 * Gets a date formatter instance using the specified style and 202 * locale in the default time zone. 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 * Gets a date formatter instance using the specified style and 217 * time zone in the default locale. 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 * Gets a date formatter instance using the specified style, time 233 * zone and locale. 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 * Gets a time formatter instance using the specified style in the 249 * default time zone and locale. 250 * 251 * @param style time style: FULL, LONG, MEDIUM, or SHORT 252 * @return a localized standard time formatter 253 * @throws IllegalArgumentException if the Locale has no time 254 * pattern defined 255 * @since 2.1 256 */ 257 public static FastDateFormat getTimeInstance(final int style) { 258 return cache.getTimeInstance(style, null, null); 259 } 260 261 /** 262 * Gets a time formatter instance using the specified style and 263 * locale in the default time zone. 264 * 265 * @param style time style: FULL, LONG, MEDIUM, or SHORT 266 * @param locale optional locale, overrides system locale 267 * @return a localized standard time formatter 268 * @throws IllegalArgumentException if the Locale has no time 269 * pattern defined 270 * @since 2.1 271 */ 272 public static FastDateFormat getTimeInstance(final int style, final Locale locale) { 273 return cache.getTimeInstance(style, null, locale); 274 } 275 276 /** 277 * Gets a time formatter instance using the specified style and 278 * time zone in the default locale. 279 * 280 * @param style time style: FULL, LONG, MEDIUM, or SHORT 281 * @param timeZone optional time zone, overrides time zone of 282 * formatted time 283 * @return a localized standard time formatter 284 * @throws IllegalArgumentException if the Locale has no time 285 * pattern defined 286 * @since 2.1 287 */ 288 public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone) { 289 return cache.getTimeInstance(style, timeZone, null); 290 } 291 292 /** 293 * Gets a time formatter instance using the specified style, time 294 * zone and locale. 295 * 296 * @param style time style: FULL, LONG, MEDIUM, or SHORT 297 * @param timeZone optional time zone, overrides time zone of 298 * formatted time 299 * @param locale optional locale, overrides system locale 300 * @return a localized standard time formatter 301 * @throws IllegalArgumentException if the Locale has no time 302 * pattern defined 303 */ 304 public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone, final Locale locale) { 305 return cache.getTimeInstance(style, timeZone, locale); 306 } 307 308 /** 309 * Gets a date/time formatter instance using the specified style 310 * in the default time zone and locale. 311 * 312 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT 313 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT 314 * @return a localized standard date/time formatter 315 * @throws IllegalArgumentException if the Locale has no date/time 316 * pattern defined 317 * @since 2.1 318 */ 319 public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle) { 320 return cache.getDateTimeInstance(dateStyle, timeStyle, null, null); 321 } 322 323 /** 324 * Gets a date/time formatter instance using the specified style and 325 * locale in the default time zone. 326 * 327 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT 328 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT 329 * @param locale optional locale, overrides system locale 330 * @return a localized standard date/time formatter 331 * @throws IllegalArgumentException if the Locale has no date/time 332 * pattern defined 333 * @since 2.1 334 */ 335 public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final Locale locale) { 336 return cache.getDateTimeInstance(dateStyle, timeStyle, null, locale); 337 } 338 339 /** 340 * Gets a date/time formatter instance using the specified style and 341 * time zone in the default locale. 342 * 343 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT 344 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT 345 * @param timeZone optional time zone, overrides time zone of 346 * formatted date 347 * @return a localized standard date/time formatter 348 * @throws IllegalArgumentException if the Locale has no date/time 349 * pattern defined 350 * @since 2.1 351 */ 352 public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone) { 353 return getDateTimeInstance(dateStyle, timeStyle, timeZone, null); 354 } 355 /** 356 * Gets a date/time formatter instance using the specified style, 357 * time zone and locale. 358 * 359 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT 360 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT 361 * @param timeZone optional time zone, overrides time zone of 362 * formatted date 363 * @param locale optional locale, overrides system locale 364 * @return a localized standard date/time formatter 365 * @throws IllegalArgumentException if the Locale has no date/time 366 * pattern defined 367 */ 368 public static FastDateFormat getDateTimeInstance( 369 final int dateStyle, final int timeStyle, final TimeZone timeZone, final Locale locale) { 370 return cache.getDateTimeInstance(dateStyle, timeStyle, timeZone, locale); 371 } 372 373 // Constructor 374 /** 375 * Constructs a new FastDateFormat. 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 * Constructs a new FastDateFormat. 389 * 390 * @param pattern {@link java.text.SimpleDateFormat} compatible pattern 391 * @param timeZone non-null time zone to use 392 * @param locale non-null locale to use 393 * @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 394 * @throws NullPointerException if pattern, timeZone, or locale is null. 395 */ 396 protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale, final Date centuryStart) { 397 printer = new FastDatePrinter(pattern, timeZone, locale); 398 parser = new FastDateParser(pattern, timeZone, locale, centuryStart); 399 } 400 401 // Format methods 402 /** 403 * Formats a {@link Date}, {@link Calendar} or 404 * {@link Long} (milliseconds) object. 405 * This method is an implementation of {@link Format#format(Object, StringBuffer, FieldPosition)} 406 * 407 * @param obj the object to format 408 * @param toAppendTo the buffer to append to 409 * @param pos the position - ignored 410 * @return the buffer passed in 411 */ 412 @Override 413 public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) { 414 return toAppendTo.append(printer.format(obj)); 415 } 416 417 /** 418 * Formats a millisecond {@code long} value. 419 * 420 * @param millis the millisecond value to format 421 * @return the formatted string 422 * @since 2.1 423 */ 424 @Override 425 public String format(final long millis) { 426 return printer.format(millis); 427 } 428 429 /** 430 * Formats a {@link Date} object using a {@link GregorianCalendar}. 431 * 432 * @param date the date to format 433 * @return the formatted string 434 */ 435 @Override 436 public String format(final Date date) { 437 return printer.format(date); 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 millisecond {@code long} value into the 453 * supplied {@link StringBuffer}. 454 * 455 * @param millis the millisecond value to format 456 * @param buf the buffer to format into 457 * @return the specified string buffer 458 * @since 2.1 459 * @deprecated Use {{@link #format(long, Appendable)}. 460 */ 461 @Deprecated 462 @Override 463 public StringBuffer format(final long millis, final StringBuffer buf) { 464 return printer.format(millis, buf); 465 } 466 467 /** 468 * Formats a {@link Date} object into the 469 * supplied {@link StringBuffer} using a {@link GregorianCalendar}. 470 * 471 * @param date the date to format 472 * @param buf the buffer to format into 473 * @return the specified string buffer 474 * @deprecated Use {{@link #format(Date, Appendable)}. 475 */ 476 @Deprecated 477 @Override 478 public StringBuffer format(final Date date, final StringBuffer buf) { 479 return printer.format(date, buf); 480 } 481 482 /** 483 * Formats a {@link Calendar} object into the 484 * supplied {@link StringBuffer}. 485 * 486 * @param calendar the calendar to format 487 * @param buf the buffer to format into 488 * @return the specified string buffer 489 * @deprecated Use {{@link #format(Calendar, Appendable)}. 490 */ 491 @Deprecated 492 @Override 493 public StringBuffer format(final Calendar calendar, final StringBuffer buf) { 494 return printer.format(calendar, buf); 495 } 496 497 /** 498 * Formats a millisecond {@code long} value into the 499 * supplied {@link StringBuffer}. 500 * 501 * @param millis the millisecond value to format 502 * @param buf the buffer to format into 503 * @return the specified string buffer 504 * @since 3.5 505 */ 506 @Override 507 public <B extends Appendable> B format(final long millis, final B buf) { 508 return printer.format(millis, buf); 509 } 510 511 /** 512 * Formats a {@link Date} object into the 513 * supplied {@link StringBuffer} using a {@link GregorianCalendar}. 514 * 515 * @param date the date to format 516 * @param buf the buffer to format into 517 * @return the specified string buffer 518 * @since 3.5 519 */ 520 @Override 521 public <B extends Appendable> B format(final Date date, final B buf) { 522 return printer.format(date, buf); 523 } 524 525 /** 526 * Formats a {@link Calendar} object into the 527 * supplied {@link StringBuffer}. 528 * 529 * @param calendar the calendar to format 530 * @param buf the buffer to format into 531 * @return the specified string buffer 532 * @since 3.5 533 */ 534 @Override 535 public <B extends Appendable> B format(final Calendar calendar, final B buf) { 536 return printer.format(calendar, buf); 537 } 538 539 // Parsing 540 541 542 /* (non-Javadoc) 543 * @see DateParser#parse(String) 544 */ 545 @Override 546 public Date parse(final String source) throws ParseException { 547 return parser.parse(source); 548 } 549 550 /* (non-Javadoc) 551 * @see DateParser#parse(String, java.text.ParsePosition) 552 */ 553 @Override 554 public Date parse(final String source, final ParsePosition pos) { 555 return parser.parse(source, pos); 556 } 557 558 /* 559 * (non-Javadoc) 560 * @see org.apache.commons.lang3.time.DateParser#parse(String, java.text.ParsePosition, java.util.Calendar) 561 */ 562 @Override 563 public boolean parse(final String source, final ParsePosition pos, final Calendar calendar) { 564 return parser.parse(source, pos, calendar); 565 } 566 567 /* (non-Javadoc) 568 * @see java.text.Format#parseObject(String, java.text.ParsePosition) 569 */ 570 @Override 571 public Object parseObject(final String source, final ParsePosition pos) { 572 return parser.parseObject(source, pos); 573 } 574 575 // Accessors 576 /** 577 * Gets the pattern used by this formatter. 578 * 579 * @return the pattern, {@link java.text.SimpleDateFormat} compatible 580 */ 581 @Override 582 public String getPattern() { 583 return printer.getPattern(); 584 } 585 586 /** 587 * Gets the time zone used by this formatter. 588 * 589 * <p>This zone is always used for {@link Date} formatting.</p> 590 * 591 * @return the time zone 592 */ 593 @Override 594 public TimeZone getTimeZone() { 595 return printer.getTimeZone(); 596 } 597 598 /** 599 * Gets the locale used by this formatter. 600 * 601 * @return the locale 602 */ 603 @Override 604 public Locale getLocale() { 605 return printer.getLocale(); 606 } 607 608 /** 609 * Gets an estimate for the maximum string length that the 610 * formatter will produce. 611 * 612 * <p>The actual formatted length will almost always be less than or 613 * equal to this amount.</p> 614 * 615 * @return the maximum formatted length 616 */ 617 public int getMaxLengthEstimate() { 618 return printer.getMaxLengthEstimate(); 619 } 620 621 // Basics 622 /** 623 * Compares two objects for equality. 624 * 625 * @param obj the object to compare to 626 * @return {@code true} if equal 627 */ 628 @Override 629 public boolean equals(final Object obj) { 630 if (!(obj instanceof FastDateFormat)) { 631 return false; 632 } 633 final FastDateFormat other = (FastDateFormat) obj; 634 // no need to check parser, as it has same invariants as printer 635 return printer.equals(other.printer); 636 } 637 638 /** 639 * Returns a hash code compatible with equals. 640 * 641 * @return a hash code compatible with equals 642 */ 643 @Override 644 public int hashCode() { 645 return printer.hashCode(); 646 } 647 648 /** 649 * Gets a debugging string version of this formatter. 650 * 651 * @return a debugging string 652 */ 653 @Override 654 public String toString() { 655 return "FastDateFormat[" + printer.getPattern() + "," + printer.getLocale() + "," + printer.getTimeZone().getID() + "]"; 656 } 657 658 /** 659 * Performs the formatting by applying the rules to the 660 * specified calendar. 661 * 662 * @param calendar the calendar to format 663 * @param buf the buffer to format into 664 * @return the specified string buffer 665 * @deprecated Use {@link #format(Calendar, Appendable)} 666 */ 667 @Deprecated 668 protected StringBuffer applyRules(final Calendar calendar, final StringBuffer buf) { 669 return printer.applyRules(calendar, buf); 670 } 671}