1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.lang3.time; 18 19 import java.text.DateFormat; 20 import java.text.FieldPosition; 21 import java.text.Format; 22 import java.text.ParseException; 23 import java.text.ParsePosition; 24 import java.text.SimpleDateFormat; 25 import java.util.Calendar; 26 import java.util.Date; 27 import java.util.GregorianCalendar; 28 import java.util.Locale; 29 import java.util.TimeZone; 30 31 /** 32 * FastDateFormat is a fast and thread-safe version of 33 * {@link java.text.SimpleDateFormat}. 34 * 35 * <p>To obtain an instance of FastDateFormat, use one of the static factory methods: 36 * {@link #getInstance(String, TimeZone, Locale)}, {@link #getDateInstance(int, TimeZone, Locale)}, 37 * {@link #getTimeInstance(int, TimeZone, Locale)}, or {@link #getDateTimeInstance(int, int, TimeZone, Locale)} 38 * </p> 39 * 40 * <p>Since FastDateFormat is thread safe, you can use a static member instance:</p> 41 * {@code 42 * private static final FastDateFormat DATE_FORMATTER = FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.SHORT); 43 * } 44 * 45 * <p>This class can be used as a direct replacement to 46 * {@link SimpleDateFormat} in most formatting and parsing situations. 47 * This class is especially useful in multi-threaded server environments. 48 * {@link SimpleDateFormat} is not thread-safe in any JDK version, 49 * nor will it be as Sun have closed the bug/RFE. 50 * </p> 51 * 52 * <p>All patterns are compatible with 53 * SimpleDateFormat (except time zones and some year patterns - see below).</p> 54 * 55 * <p>Since 3.2, FastDateFormat supports parsing as well as printing.</p> 56 * 57 * <p>Java 1.4 introduced a new pattern letter, {@code 'Z'}, to represent 58 * time zones in RFC822 format (eg. {@code +0800} or {@code -1100}). 59 * This pattern letter can be used here (on all JDK versions).</p> 60 * 61 * <p>In addition, the pattern {@code 'ZZ'} has been made to represent 62 * ISO 8601 extended format time zones (eg. {@code +08:00} or {@code -11:00}). 63 * This introduces a minor incompatibility with Java 1.4, but at a gain of 64 * useful functionality.</p> 65 * 66 * <p>Javadoc cites for the year pattern: <i>For formatting, if the number of 67 * pattern letters is 2, the year is truncated to 2 digits; otherwise it is 68 * interpreted as a number.</i> Starting with Java 1.7 a pattern of 'Y' or 69 * 'YYY' will be formatted as '2003', while it was '03' in former Java 70 * versions. FastDateFormat implements the behavior of Java 7.</p> 71 * 72 * @since 2.0 73 */ 74 public class FastDateFormat extends Format implements DateParser, DatePrinter { 75 76 /** 77 * Required for serialization support. 78 * 79 * @see java.io.Serializable 80 */ 81 private static final long serialVersionUID = 2L; 82 83 /** 84 * FULL locale dependent date or time style. 85 */ 86 87 public static final int FULL = DateFormat.FULL; 88 89 /** 90 * LONG locale dependent date or time style. 91 */ 92 public static final int LONG = DateFormat.LONG; 93 94 /** 95 * MEDIUM locale dependent date or time style. 96 */ 97 public static final int MEDIUM = DateFormat.MEDIUM; 98 99 /** 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 * Gets a date formatter instance using the specified style in the 113 * default time zone and locale. 114 * 115 * @param style date style: FULL, LONG, MEDIUM, or SHORT 116 * @return a localized standard date formatter 117 * @throws IllegalArgumentException if the Locale has no date 118 * pattern defined 119 * @since 2.1 120 */ 121 public static FastDateFormat getDateInstance(final int style) { 122 return cache.getDateInstance(style, null, null); 123 } 124 125 /** 126 * Gets a date formatter instance using the specified style and 127 * locale in the default time zone. 128 * 129 * @param style date style: FULL, LONG, MEDIUM, or SHORT 130 * @param locale optional locale, overrides system locale 131 * @return a localized standard date formatter 132 * @throws IllegalArgumentException if the Locale has no date 133 * pattern defined 134 * @since 2.1 135 */ 136 public static FastDateFormat getDateInstance(final int style, final Locale locale) { 137 return cache.getDateInstance(style, null, locale); 138 } 139 140 /** 141 * Gets a date formatter instance using the specified style and 142 * time zone in the default locale. 143 * 144 * @param style date style: FULL, LONG, MEDIUM, or SHORT 145 * @param timeZone optional time zone, overrides time zone of 146 * formatted date 147 * @return a localized standard date formatter 148 * @throws IllegalArgumentException if the Locale has no date 149 * pattern defined 150 * @since 2.1 151 */ 152 public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone) { 153 return cache.getDateInstance(style, timeZone, null); 154 } 155 156 /** 157 * Gets a date formatter instance using the specified style, time 158 * zone and locale. 159 * 160 * @param style date style: FULL, LONG, MEDIUM, or SHORT 161 * @param timeZone optional time zone, overrides time zone of 162 * formatted date 163 * @param locale optional locale, overrides system locale 164 * @return a localized standard date formatter 165 * @throws IllegalArgumentException if the Locale has no date 166 * pattern defined 167 */ 168 public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone, final Locale locale) { 169 return cache.getDateInstance(style, timeZone, locale); 170 } 171 172 /** 173 * Gets a date/time formatter instance using the specified style 174 * in the default time zone and locale. 175 * 176 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT 177 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT 178 * @return a localized standard date/time formatter 179 * @throws IllegalArgumentException if the Locale has no date/time 180 * pattern defined 181 * @since 2.1 182 */ 183 public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle) { 184 return cache.getDateTimeInstance(dateStyle, timeStyle, null, null); 185 } 186 187 /** 188 * Gets a date/time formatter instance using the specified style and 189 * locale in the default time zone. 190 * 191 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT 192 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT 193 * @param locale optional locale, overrides system locale 194 * @return a localized standard date/time formatter 195 * @throws IllegalArgumentException if the Locale has no date/time 196 * pattern defined 197 * @since 2.1 198 */ 199 public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final Locale locale) { 200 return cache.getDateTimeInstance(dateStyle, timeStyle, null, locale); 201 } 202 203 /** 204 * Gets a date/time formatter instance using the specified style and 205 * time zone in the default locale. 206 * 207 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT 208 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT 209 * @param timeZone optional time zone, overrides time zone of 210 * formatted date 211 * @return a localized standard date/time formatter 212 * @throws IllegalArgumentException if the Locale has no date/time 213 * pattern defined 214 * @since 2.1 215 */ 216 public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone) { 217 return getDateTimeInstance(dateStyle, timeStyle, timeZone, null); 218 } 219 220 /** 221 * Gets a date/time formatter instance using the specified style, 222 * time zone and locale. 223 * 224 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT 225 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT 226 * @param timeZone optional time zone, overrides time zone of 227 * formatted date 228 * @param locale optional locale, overrides system locale 229 * @return a localized standard date/time formatter 230 * @throws IllegalArgumentException if the Locale has no date/time 231 * pattern defined 232 */ 233 public static FastDateFormat getDateTimeInstance( 234 final int dateStyle, final int timeStyle, final TimeZone timeZone, final Locale locale) { 235 return cache.getDateTimeInstance(dateStyle, timeStyle, timeZone, locale); 236 } 237 238 /** 239 * Gets a formatter instance using the default pattern in the 240 * default locale. 241 * 242 * @return a date/time formatter 243 */ 244 public static FastDateFormat getInstance() { 245 return cache.getInstance(); 246 } 247 248 /** 249 * Gets a formatter instance using the specified pattern in the 250 * default locale. 251 * 252 * @param pattern {@link java.text.SimpleDateFormat} compatible 253 * pattern 254 * @return a pattern based date/time formatter 255 * @throws IllegalArgumentException if pattern is invalid 256 */ 257 public static FastDateFormat getInstance(final String pattern) { 258 return cache.getInstance(pattern, null, null); 259 } 260 261 /** 262 * Gets a formatter instance using the specified pattern and 263 * locale. 264 * 265 * @param pattern {@link java.text.SimpleDateFormat} compatible 266 * pattern 267 * @param locale optional locale, overrides system locale 268 * @return a pattern based date/time formatter 269 * @throws IllegalArgumentException if pattern is invalid 270 */ 271 public static FastDateFormat getInstance(final String pattern, final Locale locale) { 272 return cache.getInstance(pattern, null, locale); 273 } 274 275 /** 276 * Gets a formatter instance using the specified pattern and 277 * time zone. 278 * 279 * @param pattern {@link java.text.SimpleDateFormat} compatible 280 * pattern 281 * @param timeZone optional time zone, overrides time zone of 282 * formatted date 283 * @return a pattern based date/time formatter 284 * @throws IllegalArgumentException if pattern is invalid 285 */ 286 public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone) { 287 return cache.getInstance(pattern, timeZone, null); 288 } 289 290 /** 291 * Gets a formatter instance using the specified pattern, time zone 292 * and locale. 293 * 294 * @param pattern {@link java.text.SimpleDateFormat} compatible 295 * pattern 296 * @param timeZone optional time zone, overrides time zone of 297 * formatted date 298 * @param locale optional locale, overrides system locale 299 * @return a pattern based date/time formatter 300 * @throws IllegalArgumentException if pattern is invalid 301 * or {@code null} 302 */ 303 public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone, final Locale locale) { 304 return cache.getInstance(pattern, timeZone, locale); 305 } 306 307 /** 308 * Gets a time formatter instance using the specified style in the 309 * default time zone and locale. 310 * 311 * @param style time style: FULL, LONG, MEDIUM, or SHORT 312 * @return a localized standard time formatter 313 * @throws IllegalArgumentException if the Locale has no time 314 * pattern defined 315 * @since 2.1 316 */ 317 public static FastDateFormat getTimeInstance(final int style) { 318 return cache.getTimeInstance(style, null, null); 319 } 320 321 /** 322 * Gets a time formatter instance using the specified style and 323 * locale in the default time zone. 324 * 325 * @param style time style: FULL, LONG, MEDIUM, or SHORT 326 * @param locale optional locale, overrides system locale 327 * @return a localized standard time formatter 328 * @throws IllegalArgumentException if the Locale has no time 329 * pattern defined 330 * @since 2.1 331 */ 332 public static FastDateFormat getTimeInstance(final int style, final Locale locale) { 333 return cache.getTimeInstance(style, null, locale); 334 } 335 336 /** 337 * Gets a time formatter instance using the specified style and 338 * time zone in the default locale. 339 * 340 * @param style time style: FULL, LONG, MEDIUM, or SHORT 341 * @param timeZone optional time zone, overrides time zone of 342 * formatted time 343 * @return a localized standard time formatter 344 * @throws IllegalArgumentException if the Locale has no time 345 * pattern defined 346 * @since 2.1 347 */ 348 public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone) { 349 return cache.getTimeInstance(style, timeZone, null); 350 } 351 352 /** 353 * Gets a time formatter instance using the specified style, time 354 * zone and locale. 355 * 356 * @param style time style: FULL, LONG, MEDIUM, or SHORT 357 * @param timeZone optional time zone, overrides time zone of 358 * formatted time 359 * @param locale optional locale, overrides system locale 360 * @return a localized standard time formatter 361 * @throws IllegalArgumentException if the Locale has no time 362 * pattern defined 363 */ 364 public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone, final Locale locale) { 365 return cache.getTimeInstance(style, timeZone, locale); 366 } 367 368 /** Our fast printer. */ 369 private final FastDatePrinter printer; 370 /** Our fast parser. */ 371 private final FastDateParser parser; 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 /** 402 * Performs the formatting by applying the rules to the 403 * specified calendar. 404 * 405 * @param calendar the calendar to format 406 * @param buf the buffer to format into 407 * @return the specified string buffer 408 * @deprecated Use {@link #format(Calendar, Appendable)} 409 */ 410 @Deprecated 411 protected StringBuffer applyRules(final Calendar calendar, final StringBuffer buf) { 412 return printer.applyRules(calendar, buf); 413 } 414 415 // Basics 416 /** 417 * Compares two objects for equality. 418 * 419 * @param obj the object to compare to 420 * @return {@code true} if equal 421 */ 422 @Override 423 public boolean equals(final Object obj) { 424 if (!(obj instanceof FastDateFormat)) { 425 return false; 426 } 427 final FastDateFormat other = (FastDateFormat) obj; 428 // no need to check parser, as it has same invariants as printer 429 return printer.equals(other.printer); 430 } 431 432 /** 433 * Formats a {@link Calendar} object. 434 * 435 * @param calendar the calendar to format 436 * @return the formatted string 437 */ 438 @Override 439 public String format(final Calendar calendar) { 440 return printer.format(calendar); 441 } 442 443 /** 444 * Formats a {@link Calendar} object into the 445 * supplied {@link StringBuffer}. 446 * 447 * @param calendar the calendar to format 448 * @param buf the buffer to format into 449 * @return the specified string buffer 450 * @since 3.5 451 */ 452 @Override 453 public <B extends Appendable> B format(final Calendar calendar, final B buf) { 454 return printer.format(calendar, buf); 455 } 456 457 /** 458 * Formats a {@link Calendar} object into the 459 * supplied {@link StringBuffer}. 460 * 461 * @param calendar the calendar to format 462 * @param buf the buffer to format into 463 * @return the specified string buffer 464 * @deprecated Use {{@link #format(Calendar, Appendable)}. 465 */ 466 @Deprecated 467 @Override 468 public StringBuffer format(final Calendar calendar, final StringBuffer buf) { 469 return printer.format(calendar, buf); 470 } 471 472 /** 473 * Formats a {@link Date} object using a {@link GregorianCalendar}. 474 * 475 * @param date the date to format 476 * @return the formatted string 477 */ 478 @Override 479 public String format(final Date date) { 480 return printer.format(date); 481 } 482 483 /** 484 * Formats a {@link Date} object into the 485 * supplied {@link StringBuffer} using a {@link GregorianCalendar}. 486 * 487 * @param date the date to format 488 * @param buf the buffer to format into 489 * @return the specified string buffer 490 * @since 3.5 491 */ 492 @Override 493 public <B extends Appendable> B format(final Date date, final B buf) { 494 return printer.format(date, buf); 495 } 496 497 /** 498 * Formats a {@link Date} object into the 499 * supplied {@link StringBuffer} using a {@link GregorianCalendar}. 500 * 501 * @param date the date to format 502 * @param buf the buffer to format into 503 * @return the specified string buffer 504 * @deprecated Use {{@link #format(Date, Appendable)}. 505 */ 506 @Deprecated 507 @Override 508 public StringBuffer format(final Date date, final StringBuffer buf) { 509 return printer.format(date, buf); 510 } 511 512 /** 513 * Formats a millisecond {@code long} value. 514 * 515 * @param millis the millisecond value to format 516 * @return the formatted string 517 * @since 2.1 518 */ 519 @Override 520 public String format(final long millis) { 521 return printer.format(millis); 522 } 523 524 /** 525 * Formats a millisecond {@code long} value into the 526 * supplied {@link StringBuffer}. 527 * 528 * @param millis the millisecond value to format 529 * @param buf the buffer to format into 530 * @return the specified string buffer 531 * @since 3.5 532 */ 533 @Override 534 public <B extends Appendable> B format(final long millis, final B buf) { 535 return printer.format(millis, buf); 536 } 537 538 // Parsing 539 540 /** 541 * Formats a millisecond {@code long} value into the 542 * supplied {@link StringBuffer}. 543 * 544 * @param millis the millisecond value to format 545 * @param buf the buffer to format into 546 * @return the specified string buffer 547 * @since 2.1 548 * @deprecated Use {{@link #format(long, Appendable)}. 549 */ 550 @Deprecated 551 @Override 552 public StringBuffer format(final long millis, final StringBuffer buf) { 553 return printer.format(millis, buf); 554 } 555 556 // Format methods 557 /** 558 * Formats a {@link Date}, {@link Calendar} or 559 * {@link Long} (milliseconds) object. 560 * This method is an implementation of {@link Format#format(Object, StringBuffer, FieldPosition)} 561 * 562 * @param obj the object to format 563 * @param toAppendTo the buffer to append to 564 * @param pos the position - ignored 565 * @return the buffer passed in 566 */ 567 @Override 568 public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) { 569 return toAppendTo.append(printer.format(obj)); 570 } 571 572 /** 573 * Gets the locale used by this formatter. 574 * 575 * @return the locale 576 */ 577 @Override 578 public Locale getLocale() { 579 return printer.getLocale(); 580 } 581 582 /** 583 * Gets an estimate for the maximum string length that the 584 * formatter will produce. 585 * 586 * <p>The actual formatted length will almost always be less than or 587 * equal to this amount.</p> 588 * 589 * @return the maximum formatted length 590 */ 591 public int getMaxLengthEstimate() { 592 return printer.getMaxLengthEstimate(); 593 } 594 595 // Accessors 596 /** 597 * Gets the pattern used by this formatter. 598 * 599 * @return the pattern, {@link java.text.SimpleDateFormat} compatible 600 */ 601 @Override 602 public String getPattern() { 603 return printer.getPattern(); 604 } 605 606 /** 607 * Gets the time zone used by this formatter. 608 * 609 * <p>This zone is always used for {@link Date} formatting.</p> 610 * 611 * @return the time zone 612 */ 613 @Override 614 public TimeZone getTimeZone() { 615 return printer.getTimeZone(); 616 } 617 618 /** 619 * Returns a hash code compatible with equals. 620 * 621 * @return a hash code compatible with equals 622 */ 623 @Override 624 public int hashCode() { 625 return printer.hashCode(); 626 } 627 628 /* (non-Javadoc) 629 * @see DateParser#parse(String) 630 */ 631 @Override 632 public Date parse(final String source) throws ParseException { 633 return parser.parse(source); 634 } 635 636 /* (non-Javadoc) 637 * @see DateParser#parse(String, java.text.ParsePosition) 638 */ 639 @Override 640 public Date parse(final String source, final ParsePosition pos) { 641 return parser.parse(source, pos); 642 } 643 644 /* 645 * (non-Javadoc) 646 * @see org.apache.commons.lang3.time.DateParser#parse(String, java.text.ParsePosition, java.util.Calendar) 647 */ 648 @Override 649 public boolean parse(final String source, final ParsePosition pos, final Calendar calendar) { 650 return parser.parse(source, pos, calendar); 651 } 652 653 /* (non-Javadoc) 654 * @see java.text.Format#parseObject(String, java.text.ParsePosition) 655 */ 656 @Override 657 public Object parseObject(final String source, final ParsePosition pos) { 658 return parser.parseObject(source, pos); 659 } 660 661 /** 662 * Gets a debugging string version of this formatter. 663 * 664 * @return a debugging string 665 */ 666 @Override 667 public String toString() { 668 return "FastDateFormat[" + printer.getPattern() + "," + printer.getLocale() + "," + printer.getTimeZone().getID() + "]"; 669 } 670 }