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.util.Calendar; 025import java.util.Date; 026import java.util.Locale; 027import java.util.TimeZone; 028 029/** 030 * <p>FastDateFormat is a fast and thread-safe version of 031 * {@link java.text.SimpleDateFormat}.</p> 032 * 033 * <p>To obtain an instance of FastDateFormat, use one of the static factory methods: 034 * {@link #getInstance(String, TimeZone, Locale)}, {@link #getDateInstance(int, TimeZone, Locale)}, 035 * {@link #getTimeInstance(int, TimeZone, Locale)}, or {@link #getDateTimeInstance(int, int, TimeZone, Locale)} 036 * </p> 037 * 038 * <p>Since FastDateFormat is thread safe, you can use a static member instance:</p> 039 * <code> 040 * private static final FastDateFormat DATE_FORMATTER = FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.SHORT); 041 * </code> 042 * 043 * <p>This class can be used as a direct replacement to 044 * {@code SimpleDateFormat} in most formatting and parsing situations. 045 * This class is especially useful in multi-threaded server environments. 046 * {@code SimpleDateFormat} is not thread-safe in any JDK version, 047 * nor will it be as Sun have closed the bug/RFE. 048 * </p> 049 * 050 * <p>All patterns are compatible with 051 * SimpleDateFormat (except time zones and some year patterns - see below).</p> 052 * 053 * <p>Since 3.2, FastDateFormat supports parsing as well as printing.</p> 054 * 055 * <p>Java 1.4 introduced a new pattern letter, {@code 'Z'}, to represent 056 * time zones in RFC822 format (eg. {@code +0800} or {@code -1100}). 057 * This pattern letter can be used here (on all JDK versions).</p> 058 * 059 * <p>In addition, the pattern {@code 'ZZ'} has been made to represent 060 * ISO 8601 extended format time zones (eg. {@code +08:00} or {@code -11:00}). 061 * This introduces a minor incompatibility with Java 1.4, but at a gain of 062 * useful functionality.</p> 063 * 064 * <p>Javadoc cites for the year pattern: <i>For formatting, if the number of 065 * pattern letters is 2, the year is truncated to 2 digits; otherwise it is 066 * interpreted as a number.</i> Starting with Java 1.7 a pattern of 'Y' or 067 * 'YYY' will be formatted as '2003', while it was '03' in former Java 068 * versions. FastDateFormat implements the behavior of Java 7.</p> 069 * 070 * @since 2.0 071 */ 072public class FastDateFormat extends Format implements DateParser, DatePrinter { 073 074 /** 075 * Required for serialization support. 076 * 077 * @see java.io.Serializable 078 */ 079 private static final long serialVersionUID = 2L; 080 081 /** 082 * FULL locale dependent date or time style. 083 */ 084 085 public static final int FULL = DateFormat.FULL; 086 087 /** 088 * LONG locale dependent date or time style. 089 */ 090 public static final int LONG = DateFormat.LONG; 091 092 /** 093 * MEDIUM locale dependent date or time style. 094 */ 095 public static final int MEDIUM = DateFormat.MEDIUM; 096 097 /** 098 * SHORT locale dependent date or time style. 099 */ 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, StringBuffer, 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 StringBuffer format(final Object obj, final StringBuffer 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 2.1 461 * @deprecated Use {{@link #format(long, Appendable)}. 462 */ 463 @Deprecated 464 @Override 465 public StringBuffer format(final long millis, final StringBuffer buf) { 466 return printer.format(millis, buf); 467 } 468 469 /** 470 * <p>Formats a {@code Date} object into the 471 * supplied {@code StringBuffer} using a {@code GregorianCalendar}.</p> 472 * 473 * @param date the date to format 474 * @param buf the buffer to format into 475 * @return the specified string buffer 476 * @deprecated Use {{@link #format(Date, Appendable)}. 477 */ 478 @Deprecated 479 @Override 480 public StringBuffer format(final Date date, final StringBuffer 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 * @deprecated Use {{@link #format(Calendar, Appendable)}. 492 */ 493 @Deprecated 494 @Override 495 public StringBuffer format(final Calendar calendar, final StringBuffer buf) { 496 return printer.format(calendar, buf); 497 } 498 499 /** 500 * <p>Formats a millisecond {@code long} value into the 501 * supplied {@code StringBuffer}.</p> 502 * 503 * @param millis the millisecond value to format 504 * @param buf the buffer to format into 505 * @return the specified string buffer 506 * @since 3.5 507 */ 508 @Override 509 public <B extends Appendable> B format(final long millis, final B buf) { 510 return printer.format(millis, buf); 511 } 512 513 /** 514 * <p>Formats a {@code Date} object into the 515 * supplied {@code StringBuffer} using a {@code GregorianCalendar}.</p> 516 * 517 * @param date the date to format 518 * @param buf the buffer to format into 519 * @return the specified string buffer 520 * @since 3.5 521 */ 522 @Override 523 public <B extends Appendable> B format(final Date date, final B buf) { 524 return printer.format(date, buf); 525 } 526 527 /** 528 * <p>Formats a {@code Calendar} object into the 529 * supplied {@code StringBuffer}.</p> 530 * 531 * @param calendar the calendar to format 532 * @param buf the buffer to format into 533 * @return the specified string buffer 534 * @since 3.5 535 */ 536 @Override 537 public <B extends Appendable> B format(final Calendar calendar, final B buf) { 538 return printer.format(calendar, buf); 539 } 540 541 // Parsing 542 //----------------------------------------------------------------------- 543 544 545 /* (non-Javadoc) 546 * @see DateParser#parse(java.lang.String) 547 */ 548 @Override 549 public Date parse(final String source) throws ParseException { 550 return parser.parse(source); 551 } 552 553 /* (non-Javadoc) 554 * @see DateParser#parse(java.lang.String, java.text.ParsePosition) 555 */ 556 @Override 557 public Date parse(final String source, final ParsePosition pos) { 558 return parser.parse(source, pos); 559 } 560 561 /* 562 * (non-Javadoc) 563 * @see org.apache.commons.lang3.time.DateParser#parse(java.lang.String, java.text.ParsePosition, java.util.Calendar) 564 */ 565 @Override 566 public boolean parse(final String source, final ParsePosition pos, final Calendar calendar) { 567 return parser.parse(source, pos, calendar); 568 } 569 570 /* (non-Javadoc) 571 * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition) 572 */ 573 @Override 574 public Object parseObject(final String source, final ParsePosition pos) { 575 return parser.parseObject(source, pos); 576 } 577 578 // Accessors 579 //----------------------------------------------------------------------- 580 /** 581 * <p>Gets the pattern used by this formatter.</p> 582 * 583 * @return the pattern, {@link java.text.SimpleDateFormat} compatible 584 */ 585 @Override 586 public String getPattern() { 587 return printer.getPattern(); 588 } 589 590 /** 591 * <p>Gets the time zone used by this formatter.</p> 592 * 593 * <p>This zone is always used for {@code Date} formatting. </p> 594 * 595 * @return the time zone 596 */ 597 @Override 598 public TimeZone getTimeZone() { 599 return printer.getTimeZone(); 600 } 601 602 /** 603 * <p>Gets the locale used by this formatter.</p> 604 * 605 * @return the locale 606 */ 607 @Override 608 public Locale getLocale() { 609 return printer.getLocale(); 610 } 611 612 /** 613 * <p>Gets an estimate for the maximum string length that the 614 * formatter will produce.</p> 615 * 616 * <p>The actual formatted length will almost always be less than or 617 * equal to this amount.</p> 618 * 619 * @return the maximum formatted length 620 */ 621 public int getMaxLengthEstimate() { 622 return printer.getMaxLengthEstimate(); 623 } 624 625 // Basics 626 //----------------------------------------------------------------------- 627 /** 628 * <p>Compares two objects for equality.</p> 629 * 630 * @param obj the object to compare to 631 * @return {@code true} if equal 632 */ 633 @Override 634 public boolean equals(final Object obj) { 635 if (!(obj instanceof FastDateFormat)) { 636 return false; 637 } 638 final FastDateFormat other = (FastDateFormat) obj; 639 // no need to check parser, as it has same invariants as printer 640 return printer.equals(other.printer); 641 } 642 643 /** 644 * <p>Returns a hash code compatible with equals.</p> 645 * 646 * @return a hash code compatible with equals 647 */ 648 @Override 649 public int hashCode() { 650 return printer.hashCode(); 651 } 652 653 /** 654 * <p>Gets a debugging string version of this formatter.</p> 655 * 656 * @return a debugging string 657 */ 658 @Override 659 public String toString() { 660 return "FastDateFormat[" + printer.getPattern() + "," + printer.getLocale() + "," + printer.getTimeZone().getID() + "]"; 661 } 662 663 /** 664 * <p>Performs the formatting by applying the rules to the 665 * specified calendar.</p> 666 * 667 * @param calendar the calendar to format 668 * @param buf the buffer to format into 669 * @return the specified string buffer 670 * @deprecated Use {@link #format(Calendar, Appendable)} 671 */ 672 @Deprecated 673 protected StringBuffer applyRules(final Calendar calendar, final StringBuffer buf) { 674 return printer.applyRules(calendar, buf); 675 } 676}