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 1533798 2013-10-19 17:46:57Z tn $ 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 printer= new FastDatePrinter(pattern, timeZone, locale); 370 parser= new FastDateParser(pattern, timeZone, locale); 371 } 372 373 // Format methods 374 //----------------------------------------------------------------------- 375 /** 376 * <p>Formats a {@code Date}, {@code Calendar} or 377 * {@code Long} (milliseconds) object.</p> 378 * 379 * @param obj the object to format 380 * @param toAppendTo the buffer to append to 381 * @param pos the position - ignored 382 * @return the buffer passed in 383 */ 384 @Override 385 public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) { 386 return printer.format(obj, toAppendTo, pos); 387 } 388 389 /** 390 * <p>Formats a millisecond {@code long} value.</p> 391 * 392 * @param millis the millisecond value to format 393 * @return the formatted string 394 * @since 2.1 395 */ 396 @Override 397 public String format(final long millis) { 398 return printer.format(millis); 399 } 400 401 /** 402 * <p>Formats a {@code Date} object using a {@code GregorianCalendar}.</p> 403 * 404 * @param date the date to format 405 * @return the formatted string 406 */ 407 @Override 408 public String format(final Date date) { 409 return printer.format(date); 410 } 411 412 /** 413 * <p>Formats a {@code Calendar} object.</p> 414 * 415 * @param calendar the calendar to format 416 * @return the formatted string 417 */ 418 @Override 419 public String format(final Calendar calendar) { 420 return printer.format(calendar); 421 } 422 423 /** 424 * <p>Formats a millisecond {@code long} value into the 425 * supplied {@code StringBuffer}.</p> 426 * 427 * @param millis the millisecond value to format 428 * @param buf the buffer to format into 429 * @return the specified string buffer 430 * @since 2.1 431 */ 432 @Override 433 public StringBuffer format(final long millis, final StringBuffer buf) { 434 return printer.format(millis, buf); 435 } 436 437 /** 438 * <p>Formats a {@code Date} object into the 439 * supplied {@code StringBuffer} using a {@code GregorianCalendar}.</p> 440 * 441 * @param date the date to format 442 * @param buf the buffer to format into 443 * @return the specified string buffer 444 */ 445 @Override 446 public StringBuffer format(final Date date, final StringBuffer buf) { 447 return printer.format(date, buf); 448 } 449 450 /** 451 * <p>Formats a {@code Calendar} object into the 452 * supplied {@code StringBuffer}.</p> 453 * 454 * @param calendar the calendar to format 455 * @param buf the buffer to format into 456 * @return the specified string buffer 457 */ 458 @Override 459 public StringBuffer format(final Calendar calendar, final StringBuffer buf) { 460 return printer.format(calendar, buf); 461 } 462 463 // Parsing 464 //----------------------------------------------------------------------- 465 466 467 /* (non-Javadoc) 468 * @see DateParser#parse(java.lang.String) 469 */ 470 @Override 471 public Date parse(final String source) throws ParseException { 472 return parser.parse(source); 473 } 474 475 /* (non-Javadoc) 476 * @see DateParser#parse(java.lang.String, java.text.ParsePosition) 477 */ 478 @Override 479 public Date parse(final String source, final ParsePosition pos) { 480 return parser.parse(source, pos); 481 } 482 483 /* (non-Javadoc) 484 * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition) 485 */ 486 @Override 487 public Object parseObject(final String source, final ParsePosition pos) { 488 return parser.parseObject(source, pos); 489 } 490 491 // Accessors 492 //----------------------------------------------------------------------- 493 /** 494 * <p>Gets the pattern used by this formatter.</p> 495 * 496 * @return the pattern, {@link java.text.SimpleDateFormat} compatible 497 */ 498 @Override 499 public String getPattern() { 500 return printer.getPattern(); 501 } 502 503 /** 504 * <p>Gets the time zone used by this formatter.</p> 505 * 506 * <p>This zone is always used for {@code Date} formatting. </p> 507 * 508 * @return the time zone 509 */ 510 @Override 511 public TimeZone getTimeZone() { 512 return printer.getTimeZone(); 513 } 514 515 /** 516 * <p>Gets the locale used by this formatter.</p> 517 * 518 * @return the locale 519 */ 520 @Override 521 public Locale getLocale() { 522 return printer.getLocale(); 523 } 524 525 /** 526 * <p>Gets an estimate for the maximum string length that the 527 * formatter will produce.</p> 528 * 529 * <p>The actual formatted length will almost always be less than or 530 * equal to this amount.</p> 531 * 532 * @return the maximum formatted length 533 */ 534 public int getMaxLengthEstimate() { 535 return printer.getMaxLengthEstimate(); 536 } 537 538 // Basics 539 //----------------------------------------------------------------------- 540 /** 541 * <p>Compares two objects for equality.</p> 542 * 543 * @param obj the object to compare to 544 * @return {@code true} if equal 545 */ 546 @Override 547 public boolean equals(final Object obj) { 548 if (obj instanceof FastDateFormat == false) { 549 return false; 550 } 551 final FastDateFormat other = (FastDateFormat) obj; 552 // no need to check parser, as it has same invariants as printer 553 return printer.equals(other.printer); 554 } 555 556 /** 557 * <p>Returns a hashcode compatible with equals.</p> 558 * 559 * @return a hashcode compatible with equals 560 */ 561 @Override 562 public int hashCode() { 563 return printer.hashCode(); 564 } 565 566 /** 567 * <p>Gets a debugging string version of this formatter.</p> 568 * 569 * @return a debugging string 570 */ 571 @Override 572 public String toString() { 573 return "FastDateFormat[" + printer.getPattern() + "," + printer.getLocale() + "," + printer.getTimeZone().getID() + "]"; 574 } 575 576 577 /** 578 * <p>Performs the formatting by applying the rules to the 579 * specified calendar.</p> 580 * 581 * @param calendar the calendar to format 582 * @param buf the buffer to format into 583 * @return the specified string buffer 584 */ 585 protected StringBuffer applyRules(final Calendar calendar, final StringBuffer buf) { 586 return printer.applyRules(calendar, buf); 587 } 588 589 590}