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 * Required for serialization support. 075 * 076 * @see java.io.Serializable 077 */ 078 private static final long serialVersionUID = 2L; 079 080 /** 081 * FULL locale dependent date or time style. 082 */ 083 public static final int FULL = DateFormat.FULL; 084 /** 085 * LONG locale dependent date or time style. 086 */ 087 public static final int LONG = DateFormat.LONG; 088 /** 089 * MEDIUM locale dependent date or time style. 090 */ 091 public static final int MEDIUM = DateFormat.MEDIUM; 092 /** 093 * SHORT locale dependent date or time style. 094 */ 095 public static final int SHORT = DateFormat.SHORT; 096 097 private static final FormatCache<FastDateFormat> cache= new FormatCache<FastDateFormat>() { 098 @Override 099 protected FastDateFormat createInstance(final String pattern, final TimeZone timeZone, final Locale locale) { 100 return new FastDateFormat(pattern, timeZone, locale); 101 } 102 }; 103 104 private final FastDatePrinter printer; 105 private final FastDateParser parser; 106 107 //----------------------------------------------------------------------- 108 /** 109 * <p>Gets a formatter instance using the default pattern in the 110 * default locale.</p> 111 * 112 * @return a date/time formatter 113 */ 114 public static FastDateFormat getInstance() { 115 return cache.getInstance(); 116 } 117 118 /** 119 * <p>Gets a formatter instance using the specified pattern in the 120 * default locale.</p> 121 * 122 * @param pattern {@link java.text.SimpleDateFormat} compatible 123 * pattern 124 * @return a pattern based date/time formatter 125 * @throws IllegalArgumentException if pattern is invalid 126 */ 127 public static FastDateFormat getInstance(final String pattern) { 128 return cache.getInstance(pattern, null, null); 129 } 130 131 /** 132 * <p>Gets a formatter instance using the specified pattern and 133 * time zone.</p> 134 * 135 * @param pattern {@link java.text.SimpleDateFormat} compatible 136 * pattern 137 * @param timeZone optional time zone, overrides time zone of 138 * formatted date 139 * @return a pattern based date/time formatter 140 * @throws IllegalArgumentException if pattern is invalid 141 */ 142 public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone) { 143 return cache.getInstance(pattern, timeZone, null); 144 } 145 146 /** 147 * <p>Gets a formatter instance using the specified pattern and 148 * locale.</p> 149 * 150 * @param pattern {@link java.text.SimpleDateFormat} compatible 151 * pattern 152 * @param locale optional locale, overrides system locale 153 * @return a pattern based date/time formatter 154 * @throws IllegalArgumentException if pattern is invalid 155 */ 156 public static FastDateFormat getInstance(final String pattern, final Locale locale) { 157 return cache.getInstance(pattern, null, locale); 158 } 159 160 /** 161 * <p>Gets a formatter instance using the specified pattern, time zone 162 * and locale.</p> 163 * 164 * @param pattern {@link java.text.SimpleDateFormat} compatible 165 * pattern 166 * @param timeZone optional time zone, overrides time zone of 167 * formatted date 168 * @param locale optional locale, overrides system locale 169 * @return a pattern based date/time formatter 170 * @throws IllegalArgumentException if pattern is invalid 171 * or {@code null} 172 */ 173 public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone, final Locale locale) { 174 return cache.getInstance(pattern, timeZone, locale); 175 } 176 177 //----------------------------------------------------------------------- 178 /** 179 * <p>Gets a date formatter instance using the specified style in the 180 * default time zone and locale.</p> 181 * 182 * @param style date style: FULL, LONG, MEDIUM, or SHORT 183 * @return a localized standard date formatter 184 * @throws IllegalArgumentException if the Locale has no date 185 * pattern defined 186 * @since 2.1 187 */ 188 public static FastDateFormat getDateInstance(final int style) { 189 return cache.getDateInstance(style, null, null); 190 } 191 192 /** 193 * <p>Gets a date formatter instance using the specified style and 194 * locale in the default time zone.</p> 195 * 196 * @param style date style: FULL, LONG, MEDIUM, or SHORT 197 * @param locale optional locale, overrides system locale 198 * @return a localized standard date formatter 199 * @throws IllegalArgumentException if the Locale has no date 200 * pattern defined 201 * @since 2.1 202 */ 203 public static FastDateFormat getDateInstance(final int style, final Locale locale) { 204 return cache.getDateInstance(style, null, locale); 205 } 206 207 /** 208 * <p>Gets a date formatter instance using the specified style and 209 * time zone in the default locale.</p> 210 * 211 * @param style date style: FULL, LONG, MEDIUM, or SHORT 212 * @param timeZone optional time zone, overrides time zone of 213 * formatted date 214 * @return a localized standard date formatter 215 * @throws IllegalArgumentException if the Locale has no date 216 * pattern defined 217 * @since 2.1 218 */ 219 public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone) { 220 return cache.getDateInstance(style, timeZone, null); 221 } 222 223 /** 224 * <p>Gets a date formatter instance using the specified style, time 225 * zone and locale.</p> 226 * 227 * @param style date style: FULL, LONG, MEDIUM, or SHORT 228 * @param timeZone optional time zone, overrides time zone of 229 * formatted date 230 * @param locale optional locale, overrides system locale 231 * @return a localized standard date formatter 232 * @throws IllegalArgumentException if the Locale has no date 233 * pattern defined 234 */ 235 public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone, final Locale locale) { 236 return cache.getDateInstance(style, timeZone, locale); 237 } 238 239 //----------------------------------------------------------------------- 240 /** 241 * <p>Gets a time formatter instance using the specified style in the 242 * default time zone and locale.</p> 243 * 244 * @param style time style: FULL, LONG, MEDIUM, or SHORT 245 * @return a localized standard time formatter 246 * @throws IllegalArgumentException if the Locale has no time 247 * pattern defined 248 * @since 2.1 249 */ 250 public static FastDateFormat getTimeInstance(final int style) { 251 return cache.getTimeInstance(style, null, null); 252 } 253 254 /** 255 * <p>Gets a time formatter instance using the specified style and 256 * locale in the default time zone.</p> 257 * 258 * @param style time style: FULL, LONG, MEDIUM, or SHORT 259 * @param locale optional locale, overrides system locale 260 * @return a localized standard time formatter 261 * @throws IllegalArgumentException if the Locale has no time 262 * pattern defined 263 * @since 2.1 264 */ 265 public static FastDateFormat getTimeInstance(final int style, final Locale locale) { 266 return cache.getTimeInstance(style, null, locale); 267 } 268 269 /** 270 * <p>Gets a time formatter instance using the specified style and 271 * time zone in the default locale.</p> 272 * 273 * @param style time style: FULL, LONG, MEDIUM, or SHORT 274 * @param timeZone optional time zone, overrides time zone of 275 * formatted time 276 * @return a localized standard time formatter 277 * @throws IllegalArgumentException if the Locale has no time 278 * pattern defined 279 * @since 2.1 280 */ 281 public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone) { 282 return cache.getTimeInstance(style, timeZone, null); 283 } 284 285 /** 286 * <p>Gets a time formatter instance using the specified style, time 287 * zone and locale.</p> 288 * 289 * @param style time style: FULL, LONG, MEDIUM, or SHORT 290 * @param timeZone optional time zone, overrides time zone of 291 * formatted time 292 * @param locale optional locale, overrides system locale 293 * @return a localized standard time formatter 294 * @throws IllegalArgumentException if the Locale has no time 295 * pattern defined 296 */ 297 public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone, final Locale locale) { 298 return cache.getTimeInstance(style, timeZone, locale); 299 } 300 301 //----------------------------------------------------------------------- 302 /** 303 * <p>Gets a date/time formatter instance using the specified style 304 * in the default time zone and locale.</p> 305 * 306 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT 307 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT 308 * @return a localized standard date/time formatter 309 * @throws IllegalArgumentException if the Locale has no date/time 310 * pattern defined 311 * @since 2.1 312 */ 313 public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle) { 314 return cache.getDateTimeInstance(dateStyle, timeStyle, null, null); 315 } 316 317 /** 318 * <p>Gets a date/time formatter instance using the specified style and 319 * locale in the default time zone.</p> 320 * 321 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT 322 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT 323 * @param locale optional locale, overrides system locale 324 * @return a localized standard date/time formatter 325 * @throws IllegalArgumentException if the Locale has no date/time 326 * pattern defined 327 * @since 2.1 328 */ 329 public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final Locale locale) { 330 return cache.getDateTimeInstance(dateStyle, timeStyle, null, locale); 331 } 332 333 /** 334 * <p>Gets a date/time formatter instance using the specified style and 335 * time zone in the default locale.</p> 336 * 337 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT 338 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT 339 * @param timeZone optional time zone, overrides time zone of 340 * formatted date 341 * @return a localized standard date/time formatter 342 * @throws IllegalArgumentException if the Locale has no date/time 343 * pattern defined 344 * @since 2.1 345 */ 346 public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone) { 347 return getDateTimeInstance(dateStyle, timeStyle, timeZone, null); 348 } 349 /** 350 * <p>Gets a date/time formatter instance using the specified style, 351 * time zone and locale.</p> 352 * 353 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT 354 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT 355 * @param timeZone optional time zone, overrides time zone of 356 * formatted date 357 * @param locale optional locale, overrides system locale 358 * @return a localized standard date/time formatter 359 * @throws IllegalArgumentException if the Locale has no date/time 360 * pattern defined 361 */ 362 public static FastDateFormat getDateTimeInstance( 363 final int dateStyle, final int timeStyle, final TimeZone timeZone, final Locale locale) { 364 return cache.getDateTimeInstance(dateStyle, timeStyle, timeZone, locale); 365 } 366 367 // Constructor 368 //----------------------------------------------------------------------- 369 /** 370 * <p>Constructs a new FastDateFormat.</p> 371 * 372 * @param pattern {@link java.text.SimpleDateFormat} compatible pattern 373 * @param timeZone non-null time zone to use 374 * @param locale non-null locale to use 375 * @throws NullPointerException if pattern, timeZone, or locale is null. 376 */ 377 protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale) { 378 this(pattern, timeZone, locale, null); 379 } 380 381 // Constructor 382 //----------------------------------------------------------------------- 383 /** 384 * <p>Constructs a new FastDateFormat.</p> 385 * 386 * @param pattern {@link java.text.SimpleDateFormat} compatible pattern 387 * @param timeZone non-null time zone to use 388 * @param locale non-null locale to use 389 * @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 390 * @throws NullPointerException if pattern, timeZone, or locale is null. 391 */ 392 protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale, final Date centuryStart) { 393 printer= new FastDatePrinter(pattern, timeZone, locale); 394 parser= new FastDateParser(pattern, timeZone, locale, centuryStart); 395 } 396 397 // Format methods 398 //----------------------------------------------------------------------- 399 /** 400 * <p>Formats a {@code Date}, {@code Calendar} or 401 * {@code Long} (milliseconds) object.</p> 402 * This method is an implementation of {@link Format#format(Object, StringBuffer, FieldPosition)} 403 * 404 * @param obj the object to format 405 * @param toAppendTo the buffer to append to 406 * @param pos the position - ignored 407 * @return the buffer passed in 408 */ 409 @Override 410 public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) { 411 return toAppendTo.append(printer.format(obj)); 412 } 413 414 /** 415 * <p>Formats a millisecond {@code long} value.</p> 416 * 417 * @param millis the millisecond value to format 418 * @return the formatted string 419 * @since 2.1 420 */ 421 @Override 422 public String format(final long millis) { 423 return printer.format(millis); 424 } 425 426 /** 427 * <p>Formats a {@code Date} object using a {@code GregorianCalendar}.</p> 428 * 429 * @param date the date to format 430 * @return the formatted string 431 */ 432 @Override 433 public String format(final Date date) { 434 return printer.format(date); 435 } 436 437 /** 438 * <p>Formats a {@code Calendar} object.</p> 439 * 440 * @param calendar the calendar to format 441 * @return the formatted string 442 */ 443 @Override 444 public String format(final Calendar calendar) { 445 return printer.format(calendar); 446 } 447 448 /** 449 * <p>Formats a millisecond {@code long} value into the 450 * supplied {@code StringBuffer}.</p> 451 * 452 * @param millis the millisecond value to format 453 * @param buf the buffer to format into 454 * @return the specified string buffer 455 * @since 2.1 456 * @deprecated Use {{@link #format(long, Appendable)}. 457 */ 458 @Deprecated 459 @Override 460 public StringBuffer format(final long millis, final StringBuffer buf) { 461 return printer.format(millis, buf); 462 } 463 464 /** 465 * <p>Formats a {@code Date} object into the 466 * supplied {@code StringBuffer} using a {@code GregorianCalendar}.</p> 467 * 468 * @param date the date to format 469 * @param buf the buffer to format into 470 * @return the specified string buffer 471 * @deprecated Use {{@link #format(Date, Appendable)}. 472 */ 473 @Deprecated 474 @Override 475 public StringBuffer format(final Date date, final StringBuffer buf) { 476 return printer.format(date, buf); 477 } 478 479 /** 480 * <p>Formats a {@code Calendar} object into the 481 * supplied {@code StringBuffer}.</p> 482 * 483 * @param calendar the calendar to format 484 * @param buf the buffer to format into 485 * @return the specified string buffer 486 * @deprecated Use {{@link #format(Calendar, Appendable)}. 487 */ 488 @Deprecated 489 @Override 490 public StringBuffer format(final Calendar calendar, final StringBuffer buf) { 491 return printer.format(calendar, buf); 492 } 493 494 /** 495 * <p>Formats a millisecond {@code long} value into the 496 * supplied {@code StringBuffer}.</p> 497 * 498 * @param millis the millisecond value to format 499 * @param buf the buffer to format into 500 * @return the specified string buffer 501 * @since 3.5 502 */ 503 @Override 504 public <B extends Appendable> B format(final long millis, final B buf) { 505 return printer.format(millis, buf); 506 } 507 508 /** 509 * <p>Formats a {@code Date} object into the 510 * supplied {@code StringBuffer} using a {@code GregorianCalendar}.</p> 511 * 512 * @param date the date to format 513 * @param buf the buffer to format into 514 * @return the specified string buffer 515 * @since 3.5 516 */ 517 @Override 518 public <B extends Appendable> B format(final Date date, final B buf) { 519 return printer.format(date, buf); 520 } 521 522 /** 523 * <p>Formats a {@code Calendar} object into the 524 * supplied {@code StringBuffer}.</p> 525 * 526 * @param calendar the calendar to format 527 * @param buf the buffer to format into 528 * @return the specified string buffer 529 * @since 3.5 530 */ 531 @Override 532 public <B extends Appendable> B format(final Calendar calendar, final B buf) { 533 return printer.format(calendar, buf); 534 } 535 536 // Parsing 537 //----------------------------------------------------------------------- 538 539 540 /* (non-Javadoc) 541 * @see DateParser#parse(java.lang.String) 542 */ 543 @Override 544 public Date parse(final String source) throws ParseException { 545 return parser.parse(source); 546 } 547 548 /* (non-Javadoc) 549 * @see DateParser#parse(java.lang.String, java.text.ParsePosition) 550 */ 551 @Override 552 public Date parse(final String source, final ParsePosition pos) { 553 return parser.parse(source, pos); 554 } 555 556 /* 557 * (non-Javadoc) 558 * @see org.apache.commons.lang3.time.DateParser#parse(java.lang.String, java.text.ParsePosition, java.util.Calendar) 559 */ 560 @Override 561 public boolean parse(final String source, final ParsePosition pos, final Calendar calendar) { 562 return parser.parse(source, pos, calendar); 563 } 564 565 /* (non-Javadoc) 566 * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition) 567 */ 568 @Override 569 public Object parseObject(final String source, final ParsePosition pos) { 570 return parser.parseObject(source, pos); 571 } 572 573 // Accessors 574 //----------------------------------------------------------------------- 575 /** 576 * <p>Gets the pattern used by this formatter.</p> 577 * 578 * @return the pattern, {@link java.text.SimpleDateFormat} compatible 579 */ 580 @Override 581 public String getPattern() { 582 return printer.getPattern(); 583 } 584 585 /** 586 * <p>Gets the time zone used by this formatter.</p> 587 * 588 * <p>This zone is always used for {@code Date} formatting. </p> 589 * 590 * @return the time zone 591 */ 592 @Override 593 public TimeZone getTimeZone() { 594 return printer.getTimeZone(); 595 } 596 597 /** 598 * <p>Gets the locale used by this formatter.</p> 599 * 600 * @return the locale 601 */ 602 @Override 603 public Locale getLocale() { 604 return printer.getLocale(); 605 } 606 607 /** 608 * <p>Gets an estimate for the maximum string length that the 609 * formatter will produce.</p> 610 * 611 * <p>The actual formatted length will almost always be less than or 612 * equal to this amount.</p> 613 * 614 * @return the maximum formatted length 615 */ 616 public int getMaxLengthEstimate() { 617 return printer.getMaxLengthEstimate(); 618 } 619 620 // Basics 621 //----------------------------------------------------------------------- 622 /** 623 * <p>Compares two objects for equality.</p> 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 == false) { 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 * <p>Returns a hashcode compatible with equals.</p> 640 * 641 * @return a hashcode compatible with equals 642 */ 643 @Override 644 public int hashCode() { 645 return printer.hashCode(); 646 } 647 648 /** 649 * <p>Gets a debugging string version of this formatter.</p> 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 * <p>Performs the formatting by applying the rules to the 660 * specified calendar.</p> 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}