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>This class can be used as a direct replacement to 034 * {@code SimpleDateFormat} in most formatting and parsing situations. 035 * This class is especially useful in multi-threaded server environments. 036 * {@code SimpleDateFormat} is not thread-safe in any JDK version, 037 * nor will it be as Sun have closed the bug/RFE. 038 * </p> 039 * 040 * <p>All patterns are compatible with 041 * SimpleDateFormat (except time zones and some year patterns - see below).</p> 042 * 043 * <p>Since 3.2, FastDateFormat supports parsing as well as printing.</p> 044 * 045 * <p>Java 1.4 introduced a new pattern letter, {@code 'Z'}, to represent 046 * time zones in RFC822 format (eg. {@code +0800} or {@code -1100}). 047 * This pattern letter can be used here (on all JDK versions).</p> 048 * 049 * <p>In addition, the pattern {@code 'ZZ'} has been made to represent 050 * ISO8601 full format time zones (eg. {@code +08:00} or {@code -11:00}). 051 * This introduces a minor incompatibility with Java 1.4, but at a gain of 052 * useful functionality.</p> 053 * 054 * <p>Javadoc cites for the year pattern: <i>For formatting, if the number of 055 * pattern letters is 2, the year is truncated to 2 digits; otherwise it is 056 * interpreted as a number.</i> Starting with Java 1.7 a pattern of 'Y' or 057 * 'YYY' will be formatted as '2003', while it was '03' in former Java 058 * versions. FastDateFormat implements the behavior of Java 7.</p> 059 * 060 * @since 2.0 061 * @version $Id: FastDateFormat.java 1572877 2014-02-28 08:42:25Z britter $ 062 */ 063public class FastDateFormat extends Format implements DateParser, DatePrinter { 064 /** 065 * Required for serialization support. 066 * 067 * @see java.io.Serializable 068 */ 069 private static final long serialVersionUID = 2L; 070 071 /** 072 * FULL locale dependent date or time style. 073 */ 074 public static final int FULL = DateFormat.FULL; 075 /** 076 * LONG locale dependent date or time style. 077 */ 078 public static final int LONG = DateFormat.LONG; 079 /** 080 * MEDIUM locale dependent date or time style. 081 */ 082 public static final int MEDIUM = DateFormat.MEDIUM; 083 /** 084 * SHORT locale dependent date or time style. 085 */ 086 public static final int SHORT = DateFormat.SHORT; 087 088 private static final FormatCache<FastDateFormat> cache= new FormatCache<FastDateFormat>() { 089 @Override 090 protected FastDateFormat createInstance(final String pattern, final TimeZone timeZone, final Locale locale) { 091 return new FastDateFormat(pattern, timeZone, locale); 092 } 093 }; 094 095 private final FastDatePrinter printer; 096 private final FastDateParser parser; 097 098 //----------------------------------------------------------------------- 099 /** 100 * <p>Gets a formatter instance using the default pattern in the 101 * default locale.</p> 102 * 103 * @return a date/time formatter 104 */ 105 public static FastDateFormat getInstance() { 106 return cache.getInstance(); 107 } 108 109 /** 110 * <p>Gets a formatter instance using the specified pattern in the 111 * default locale.</p> 112 * 113 * @param pattern {@link java.text.SimpleDateFormat} compatible 114 * pattern 115 * @return a pattern based date/time formatter 116 * @throws IllegalArgumentException if pattern is invalid 117 */ 118 public static FastDateFormat getInstance(final String pattern) { 119 return cache.getInstance(pattern, null, null); 120 } 121 122 /** 123 * <p>Gets a formatter instance using the specified pattern and 124 * time zone.</p> 125 * 126 * @param pattern {@link java.text.SimpleDateFormat} compatible 127 * pattern 128 * @param timeZone optional time zone, overrides time zone of 129 * formatted date 130 * @return a pattern based date/time formatter 131 * @throws IllegalArgumentException if pattern is invalid 132 */ 133 public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone) { 134 return cache.getInstance(pattern, timeZone, null); 135 } 136 137 /** 138 * <p>Gets a formatter instance using the specified pattern and 139 * locale.</p> 140 * 141 * @param pattern {@link java.text.SimpleDateFormat} compatible 142 * pattern 143 * @param locale optional locale, overrides system locale 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 Locale locale) { 148 return cache.getInstance(pattern, null, locale); 149 } 150 151 /** 152 * <p>Gets a formatter instance using the specified pattern, time zone 153 * and locale.</p> 154 * 155 * @param pattern {@link java.text.SimpleDateFormat} compatible 156 * pattern 157 * @param timeZone optional time zone, overrides time zone of 158 * formatted date 159 * @param locale optional locale, overrides system locale 160 * @return a pattern based date/time formatter 161 * @throws IllegalArgumentException if pattern is invalid 162 * or {@code null} 163 */ 164 public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone, final Locale locale) { 165 return cache.getInstance(pattern, timeZone, locale); 166 } 167 168 //----------------------------------------------------------------------- 169 /** 170 * <p>Gets a date formatter instance using the specified style in the 171 * default time zone and locale.</p> 172 * 173 * @param style date style: FULL, LONG, MEDIUM, or SHORT 174 * @return a localized standard date formatter 175 * @throws IllegalArgumentException if the Locale has no date 176 * pattern defined 177 * @since 2.1 178 */ 179 public static FastDateFormat getDateInstance(final int style) { 180 return cache.getDateInstance(style, null, null); 181 } 182 183 /** 184 * <p>Gets a date formatter instance using the specified style and 185 * locale in the default time zone.</p> 186 * 187 * @param style date style: FULL, LONG, MEDIUM, or SHORT 188 * @param locale optional locale, overrides system locale 189 * @return a localized standard date formatter 190 * @throws IllegalArgumentException if the Locale has no date 191 * pattern defined 192 * @since 2.1 193 */ 194 public static FastDateFormat getDateInstance(final int style, final Locale locale) { 195 return cache.getDateInstance(style, null, locale); 196 } 197 198 /** 199 * <p>Gets a date formatter instance using the specified style and 200 * time zone in the default locale.</p> 201 * 202 * @param style date style: FULL, LONG, MEDIUM, or SHORT 203 * @param timeZone optional time zone, overrides time zone of 204 * formatted date 205 * @return a localized standard date formatter 206 * @throws IllegalArgumentException if the Locale has no date 207 * pattern defined 208 * @since 2.1 209 */ 210 public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone) { 211 return cache.getDateInstance(style, timeZone, null); 212 } 213 214 /** 215 * <p>Gets a date formatter instance using the specified style, time 216 * zone and locale.</p> 217 * 218 * @param style date style: FULL, LONG, MEDIUM, or SHORT 219 * @param timeZone optional time zone, overrides time zone of 220 * formatted date 221 * @param locale optional locale, overrides system locale 222 * @return a localized standard date formatter 223 * @throws IllegalArgumentException if the Locale has no date 224 * pattern defined 225 */ 226 public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone, final Locale locale) { 227 return cache.getDateInstance(style, timeZone, locale); 228 } 229 230 //----------------------------------------------------------------------- 231 /** 232 * <p>Gets a time formatter instance using the specified style in the 233 * default time zone and locale.</p> 234 * 235 * @param style time style: FULL, LONG, MEDIUM, or SHORT 236 * @return a localized standard time formatter 237 * @throws IllegalArgumentException if the Locale has no time 238 * pattern defined 239 * @since 2.1 240 */ 241 public static FastDateFormat getTimeInstance(final int style) { 242 return cache.getTimeInstance(style, null, null); 243 } 244 245 /** 246 * <p>Gets a time formatter instance using the specified style and 247 * locale in the default time zone.</p> 248 * 249 * @param style time style: FULL, LONG, MEDIUM, or SHORT 250 * @param locale optional locale, overrides system locale 251 * @return a localized standard time formatter 252 * @throws IllegalArgumentException if the Locale has no time 253 * pattern defined 254 * @since 2.1 255 */ 256 public static FastDateFormat getTimeInstance(final int style, final Locale locale) { 257 return cache.getTimeInstance(style, null, locale); 258 } 259 260 /** 261 * <p>Gets a time formatter instance using the specified style and 262 * time zone in the default locale.</p> 263 * 264 * @param style time style: FULL, LONG, MEDIUM, or SHORT 265 * @param timeZone optional time zone, overrides time zone of 266 * formatted time 267 * @return a localized standard time formatter 268 * @throws IllegalArgumentException if the Locale has no time 269 * pattern defined 270 * @since 2.1 271 */ 272 public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone) { 273 return cache.getTimeInstance(style, timeZone, null); 274 } 275 276 /** 277 * <p>Gets a time formatter instance using the specified style, time 278 * zone and locale.</p> 279 * 280 * @param style time style: FULL, LONG, MEDIUM, or SHORT 281 * @param timeZone optional time zone, overrides time zone of 282 * formatted time 283 * @param locale optional locale, overrides system locale 284 * @return a localized standard time formatter 285 * @throws IllegalArgumentException if the Locale has no time 286 * pattern defined 287 */ 288 public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone, final Locale locale) { 289 return cache.getTimeInstance(style, timeZone, locale); 290 } 291 292 //----------------------------------------------------------------------- 293 /** 294 * <p>Gets a date/time formatter instance using the specified style 295 * in the default time zone and locale.</p> 296 * 297 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT 298 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT 299 * @return a localized standard date/time formatter 300 * @throws IllegalArgumentException if the Locale has no date/time 301 * pattern defined 302 * @since 2.1 303 */ 304 public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle) { 305 return cache.getDateTimeInstance(dateStyle, timeStyle, null, null); 306 } 307 308 /** 309 * <p>Gets a date/time formatter instance using the specified style and 310 * locale in the default time zone.</p> 311 * 312 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT 313 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT 314 * @param locale optional locale, overrides system locale 315 * @return a localized standard date/time formatter 316 * @throws IllegalArgumentException if the Locale has no date/time 317 * pattern defined 318 * @since 2.1 319 */ 320 public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final Locale locale) { 321 return cache.getDateTimeInstance(dateStyle, timeStyle, null, locale); 322 } 323 324 /** 325 * <p>Gets a date/time formatter instance using the specified style and 326 * time zone in the default locale.</p> 327 * 328 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT 329 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT 330 * @param timeZone optional time zone, overrides time zone of 331 * formatted date 332 * @return a localized standard date/time formatter 333 * @throws IllegalArgumentException if the Locale has no date/time 334 * pattern defined 335 * @since 2.1 336 */ 337 public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone) { 338 return getDateTimeInstance(dateStyle, timeStyle, timeZone, null); 339 } 340 /** 341 * <p>Gets a date/time formatter instance using the specified style, 342 * time zone and locale.</p> 343 * 344 * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT 345 * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT 346 * @param timeZone optional time zone, overrides time zone of 347 * formatted date 348 * @param locale optional locale, overrides system locale 349 * @return a localized standard date/time formatter 350 * @throws IllegalArgumentException if the Locale has no date/time 351 * pattern defined 352 */ 353 public static FastDateFormat getDateTimeInstance( 354 final int dateStyle, final int timeStyle, final TimeZone timeZone, final Locale locale) { 355 return cache.getDateTimeInstance(dateStyle, timeStyle, timeZone, locale); 356 } 357 358 // Constructor 359 //----------------------------------------------------------------------- 360 /** 361 * <p>Constructs a new FastDateFormat.</p> 362 * 363 * @param pattern {@link java.text.SimpleDateFormat} compatible pattern 364 * @param timeZone non-null time zone to use 365 * @param locale non-null locale to use 366 * @throws NullPointerException if pattern, timeZone, or locale is null. 367 */ 368 protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale) { 369 this(pattern, timeZone, locale, null); 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 * @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 381 * @throws NullPointerException if pattern, timeZone, or locale is null. 382 */ 383 protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale, final Date centuryStart) { 384 printer= new FastDatePrinter(pattern, timeZone, locale); 385 parser= new FastDateParser(pattern, timeZone, locale, centuryStart); 386 } 387 388 // Format methods 389 //----------------------------------------------------------------------- 390 /** 391 * <p>Formats a {@code Date}, {@code Calendar} or 392 * {@code Long} (milliseconds) object.</p> 393 * 394 * @param obj the object to format 395 * @param toAppendTo the buffer to append to 396 * @param pos the position - ignored 397 * @return the buffer passed in 398 */ 399 @Override 400 public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) { 401 return printer.format(obj, toAppendTo, pos); 402 } 403 404 /** 405 * <p>Formats a millisecond {@code long} value.</p> 406 * 407 * @param millis the millisecond value to format 408 * @return the formatted string 409 * @since 2.1 410 */ 411 @Override 412 public String format(final long millis) { 413 return printer.format(millis); 414 } 415 416 /** 417 * <p>Formats a {@code Date} object using a {@code GregorianCalendar}.</p> 418 * 419 * @param date the date to format 420 * @return the formatted string 421 */ 422 @Override 423 public String format(final Date date) { 424 return printer.format(date); 425 } 426 427 /** 428 * <p>Formats a {@code Calendar} object.</p> 429 * 430 * @param calendar the calendar to format 431 * @return the formatted string 432 */ 433 @Override 434 public String format(final Calendar calendar) { 435 return printer.format(calendar); 436 } 437 438 /** 439 * <p>Formats a millisecond {@code long} value into the 440 * supplied {@code StringBuffer}.</p> 441 * 442 * @param millis the millisecond value to format 443 * @param buf the buffer to format into 444 * @return the specified string buffer 445 * @since 2.1 446 */ 447 @Override 448 public StringBuffer format(final long millis, final StringBuffer buf) { 449 return printer.format(millis, buf); 450 } 451 452 /** 453 * <p>Formats a {@code Date} object into the 454 * supplied {@code StringBuffer} using a {@code GregorianCalendar}.</p> 455 * 456 * @param date the date to format 457 * @param buf the buffer to format into 458 * @return the specified string buffer 459 */ 460 @Override 461 public StringBuffer format(final Date date, final StringBuffer buf) { 462 return printer.format(date, buf); 463 } 464 465 /** 466 * <p>Formats a {@code Calendar} object into the 467 * supplied {@code StringBuffer}.</p> 468 * 469 * @param calendar the calendar to format 470 * @param buf the buffer to format into 471 * @return the specified string buffer 472 */ 473 @Override 474 public StringBuffer format(final Calendar calendar, final StringBuffer buf) { 475 return printer.format(calendar, buf); 476 } 477 478 // Parsing 479 //----------------------------------------------------------------------- 480 481 482 /* (non-Javadoc) 483 * @see DateParser#parse(java.lang.String) 484 */ 485 @Override 486 public Date parse(final String source) throws ParseException { 487 return parser.parse(source); 488 } 489 490 /* (non-Javadoc) 491 * @see DateParser#parse(java.lang.String, java.text.ParsePosition) 492 */ 493 @Override 494 public Date parse(final String source, final ParsePosition pos) { 495 return parser.parse(source, pos); 496 } 497 498 /* (non-Javadoc) 499 * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition) 500 */ 501 @Override 502 public Object parseObject(final String source, final ParsePosition pos) { 503 return parser.parseObject(source, pos); 504 } 505 506 // Accessors 507 //----------------------------------------------------------------------- 508 /** 509 * <p>Gets the pattern used by this formatter.</p> 510 * 511 * @return the pattern, {@link java.text.SimpleDateFormat} compatible 512 */ 513 @Override 514 public String getPattern() { 515 return printer.getPattern(); 516 } 517 518 /** 519 * <p>Gets the time zone used by this formatter.</p> 520 * 521 * <p>This zone is always used for {@code Date} formatting. </p> 522 * 523 * @return the time zone 524 */ 525 @Override 526 public TimeZone getTimeZone() { 527 return printer.getTimeZone(); 528 } 529 530 /** 531 * <p>Gets the locale used by this formatter.</p> 532 * 533 * @return the locale 534 */ 535 @Override 536 public Locale getLocale() { 537 return printer.getLocale(); 538 } 539 540 /** 541 * <p>Gets an estimate for the maximum string length that the 542 * formatter will produce.</p> 543 * 544 * <p>The actual formatted length will almost always be less than or 545 * equal to this amount.</p> 546 * 547 * @return the maximum formatted length 548 */ 549 public int getMaxLengthEstimate() { 550 return printer.getMaxLengthEstimate(); 551 } 552 553 // Basics 554 //----------------------------------------------------------------------- 555 /** 556 * <p>Compares two objects for equality.</p> 557 * 558 * @param obj the object to compare to 559 * @return {@code true} if equal 560 */ 561 @Override 562 public boolean equals(final Object obj) { 563 if (obj instanceof FastDateFormat == false) { 564 return false; 565 } 566 final FastDateFormat other = (FastDateFormat) obj; 567 // no need to check parser, as it has same invariants as printer 568 return printer.equals(other.printer); 569 } 570 571 /** 572 * <p>Returns a hashcode compatible with equals.</p> 573 * 574 * @return a hashcode compatible with equals 575 */ 576 @Override 577 public int hashCode() { 578 return printer.hashCode(); 579 } 580 581 /** 582 * <p>Gets a debugging string version of this formatter.</p> 583 * 584 * @return a debugging string 585 */ 586 @Override 587 public String toString() { 588 return "FastDateFormat[" + printer.getPattern() + "," + printer.getLocale() + "," + printer.getTimeZone().getID() + "]"; 589 } 590 591 592 /** 593 * <p>Performs the formatting by applying the rules to the 594 * specified calendar.</p> 595 * 596 * @param calendar the calendar to format 597 * @param buf the buffer to format into 598 * @return the specified string buffer 599 */ 600 protected StringBuffer applyRules(final Calendar calendar, final StringBuffer buf) { 601 return printer.applyRules(calendar, buf); 602 } 603 604 605}