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