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 * https://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 */ 017 018package org.apache.commons.lang3.time; 019 020import java.text.DateFormat; 021import java.text.FieldPosition; 022import java.text.Format; 023import java.text.ParseException; 024import java.text.ParsePosition; 025import java.text.SimpleDateFormat; 026import java.util.Calendar; 027import java.util.Date; 028import java.util.GregorianCalendar; 029import java.util.Locale; 030import java.util.TimeZone; 031 032/** 033 * FastDateFormat is a fast and thread-safe version of {@link java.text.SimpleDateFormat}. 034 * 035 * <p> 036 * To obtain an instance of FastDateFormat, use one of the static factory methods: {@link #getInstance(String, TimeZone, Locale)}, 037 * {@link #getDateInstance(int, TimeZone, Locale)}, {@link #getTimeInstance(int, TimeZone, Locale)}, or {@link #getDateTimeInstance(int, int, TimeZone, Locale)} 038 * </p> 039 * 040 * <p> 041 * Since FastDateFormat is thread safe, you can use a static member instance: 042 * </p> 043 * {@code 044 * private static final FastDateFormat DATE_FORMATTER = FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.SHORT); 045 * } 046 * 047 * <p> 048 * This class can be used as a direct replacement to {@link SimpleDateFormat} in most formatting and parsing situations. This class is especially useful in 049 * multi-threaded server environments. {@link SimpleDateFormat} is not thread-safe in any JDK version, nor will it be as Sun have closed the bug/RFE. 050 * </p> 051 * 052 * <p> 053 * Note on memory retention: unlike {@code new SimpleDateFormat(pattern)}, instances obtained from the static factory methods are held in a static cache keyed 054 * by (pattern, time zone, locale). The cache is bounded (it is flushed when it exceeds an internal limit) and can be flushed explicitly with 055 * {@link #clear()}, but each distinct key retains its instance for the lifetime of the JVM until then. Prefer fixed, application-defined patterns; do not 056 * pass unvalidated caller-supplied pattern, time zone, or locale values to the factory methods. 057 * </p> 058 * 059 * <p> 060 * All patterns are compatible with SimpleDateFormat (except time zones and some year patterns - see below). 061 * </p> 062 * 063 * <p> 064 * Since 3.2, FastDateFormat supports parsing as well as printing. 065 * </p> 066 * 067 * <p> 068 * Java 1.4 introduced a new pattern letter, {@code 'Z'}, to represent time zones in RFC822 format (for example, {@code +0800} or {@code -1100}). This pattern letter can 069 * be used here (on all JDK versions). 070 * </p> 071 * 072 * <p> 073 * In addition, the pattern {@code 'ZZ'} has been made to represent ISO 8601 extended format time zones (for example, {@code +08:00} or {@code -11:00}). This introduces 074 * a minor incompatibility with Java 1.4, but at a gain of useful functionality. 075 * </p> 076 * 077 * <p> 078 * Javadoc cites for the year pattern: <i>For formatting, if the number of pattern letters is 2, the year is truncated to 2 digits; otherwise it is interpreted 079 * as a number.</i> Starting with Java 1.7 a pattern of 'Y' or 'YYY' will be formatted as '2003', while it was '03' in former Java versions. FastDateFormat 080 * implements the behavior of Java 7. 081 * </p> 082 * 083 * @since 2.0 084 */ 085public class FastDateFormat extends Format implements DateParser, DatePrinter { 086 087 /** 088 * Required for serialization support. 089 * 090 * @see java.io.Serializable 091 */ 092 private static final long serialVersionUID = 2L; 093 094 /** 095 * FULL locale dependent date or time style. 096 */ 097 public static final int FULL = DateFormat.FULL; 098 099 /** 100 * LONG locale dependent date or time style. 101 */ 102 public static final int LONG = DateFormat.LONG; 103 104 /** 105 * MEDIUM locale dependent date or time style. 106 */ 107 public static final int MEDIUM = DateFormat.MEDIUM; 108 109 /** 110 * SHORT locale dependent date or time style. 111 */ 112 public static final int SHORT = DateFormat.SHORT; 113 114 private static final AbstractFormatCache<FastDateFormat> CACHE = new AbstractFormatCache<FastDateFormat>() { 115 116 @Override 117 protected FastDateFormat createInstance(final String pattern, final TimeZone timeZone, final Locale locale) { 118 return new FastDateFormat(pattern, timeZone, locale); 119 } 120 }; 121 122 /** 123 * Clears the caches. 124 * <p> 125 * Clears the static caches used by {@link FastDateFormat}: the (pattern, time zone, locale) to instance cache and the time zone display-name cache. 126 * Cached instances already obtained by callers remain valid; subsequent factory calls simply create and cache new instances. This can be used for 127 * operational relief if many distinct patterns, time zones, or locales have been used. 128 * </p> 129 */ 130 static void clear() { 131 AbstractFormatCache.clear(); 132 CACHE.clearInstance(); 133 FastDatePrinter.clear(); 134 } 135 136// /** 137// * Clears the static caches used by {@link FastDateFormat}: the (pattern, time zone, locale) to instance cache and the time zone display-name cache. 138// * Cached instances already obtained by callers remain valid; subsequent factory calls simply create and cache new instances. This can be used for 139// * operational relief if many distinct patterns, time zones, or locales have been used. 140// * 141// * @since 3.21.0 142// */ 143// public static void clearCache() { 144// clear(); 145// FastDatePrinter.clear(); 146// } 147 148 /** 149 * Gets a date formatter instance using the specified style in the default time zone and locale. 150 * 151 * @param style date style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}. 152 * @return A localized standard date formatter. 153 * @throws IllegalArgumentException Thrown if the Locale has no date pattern defined. 154 * @since 2.1 155 */ 156 public static FastDateFormat getDateInstance(final int style) { 157 return CACHE.getDateInstance(style, null, null); 158 } 159 160 /** 161 * Gets a date formatter instance using the specified style and locale in the default time zone. 162 * 163 * @param style date style: {@link #FULL}, LO{@link #FULL},{@link #MEDIUM}, or {@link #SHORT}. 164 * @param locale optional locale, overrides system locale. 165 * @return A localized standard date formatter. 166 * @throws IllegalArgumentException Thrown if the Locale has no date pattern defined. 167 * @since 2.1 168 */ 169 public static FastDateFormat getDateInstance(final int style, final Locale locale) { 170 return CACHE.getDateInstance(style, null, locale); 171 } 172 173 /** 174 * Gets a date formatter instance using the specified style and time zone in the default locale. 175 * 176 * @param style date style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}. 177 * @param timeZone optional time zone, overrides time zone of formatted date. 178 * @return A localized standard date formatter. 179 * @throws IllegalArgumentException Thrown if the Locale has no date pattern defined. 180 * @since 2.1 181 */ 182 public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone) { 183 return CACHE.getDateInstance(style, timeZone, null); 184 } 185 186 /** 187 * Gets a date formatter instance using the specified style, time zone and locale. 188 * 189 * @param style date style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}. 190 * @param timeZone optional time zone, overrides time zone of formatted date. 191 * @param locale optional locale, overrides system locale. 192 * @return A localized standard date formatter. 193 * @throws IllegalArgumentException Thrown if the Locale has no date pattern defined. 194 */ 195 public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone, final Locale locale) { 196 return CACHE.getDateInstance(style, timeZone, locale); 197 } 198 199 /** 200 * Gets a date/time formatter instance using the specified style in the default time zone and locale. 201 * 202 * @param dateStyle date style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}. 203 * @param timeStyle time style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}. 204 * @return A localized standard date/time formatter. 205 * @throws IllegalArgumentException Thrown if the Locale has no date/time pattern defined. 206 * @since 2.1 207 */ 208 public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle) { 209 return CACHE.getDateTimeInstance(dateStyle, timeStyle, null, null); 210 } 211 212 /** 213 * Gets a date/time formatter instance using the specified style and locale in the default time zone. 214 * 215 * @param dateStyle date style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}. 216 * @param timeStyle time style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}. 217 * @param locale optional locale, overrides system locale. 218 * @return A localized standard date/time formatter. 219 * @throws IllegalArgumentException Thrown if the Locale has no date/time pattern defined. 220 * @since 2.1 221 */ 222 public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final Locale locale) { 223 return CACHE.getDateTimeInstance(dateStyle, timeStyle, null, locale); 224 } 225 226 /** 227 * Gets a date/time formatter instance using the specified style and time zone in the default locale. 228 * 229 * @param dateStyle date style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}. 230 * @param timeStyle time style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}. 231 * @param timeZone optional time zone, overrides time zone of formatted date. 232 * @return A localized standard date/time formatter. 233 * @throws IllegalArgumentException Thrown if the Locale has no date/time pattern defined. 234 * @since 2.1 235 */ 236 public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone) { 237 return getDateTimeInstance(dateStyle, timeStyle, timeZone, null); 238 } 239 240 /** 241 * Gets a date/time formatter instance using the specified style, time zone and locale. 242 * 243 * @param dateStyle date style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}. 244 * @param timeStyle time style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}. 245 * @param timeZone optional time zone, overrides time zone of formatted date. 246 * @param locale optional locale, overrides system locale. 247 * @return A localized standard date/time formatter. 248 * @throws IllegalArgumentException Thrown if the Locale has no date/time pattern defined. 249 */ 250 public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone, final Locale locale) { 251 return CACHE.getDateTimeInstance(dateStyle, timeStyle, timeZone, locale); 252 } 253 254 /** 255 * Gets a formatter instance using the default pattern in the default locale. 256 * 257 * @return A date/time formatter. 258 */ 259 public static FastDateFormat getInstance() { 260 return CACHE.getInstance(); 261 } 262 263 /** 264 * Gets a formatter instance using the specified pattern in the default locale and time zone. 265 * 266 * @param pattern {@link java.text.SimpleDateFormat} compatible pattern. 267 * @return A pattern based date/time formatter. 268 * @throws IllegalArgumentException Thrown if pattern is invalid. 269 */ 270 public static FastDateFormat getInstance(final String pattern) { 271 return CACHE.getInstance(pattern, null, null); 272 } 273 274 /** 275 * Gets a formatter instance using the specified pattern and locale using the default time zone. 276 * 277 * @param pattern {@link java.text.SimpleDateFormat} compatible pattern. 278 * @param locale optional locale, overrides system locale. 279 * @return A pattern based date/time formatter. 280 * @throws IllegalArgumentException Thrown if pattern is invalid. 281 */ 282 public static FastDateFormat getInstance(final String pattern, final Locale locale) { 283 return CACHE.getInstance(pattern, null, locale); 284 } 285 286 /** 287 * Gets a formatter instance using the specified pattern and time zone. 288 * 289 * @param pattern {@link java.text.SimpleDateFormat} compatible pattern. 290 * @param timeZone optional time zone, overrides time zone of formatted date. 291 * @return A pattern based date/time formatter. 292 * @throws IllegalArgumentException Thrown if pattern is invalid. 293 */ 294 public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone) { 295 return CACHE.getInstance(pattern, timeZone, null); 296 } 297 298 /** 299 * Gets a formatter instance using the specified pattern, time zone and locale. 300 * 301 * @param pattern {@link java.text.SimpleDateFormat} compatible pattern. 302 * @param timeZone optional time zone, overrides time zone of formatted date. 303 * @param locale optional locale, overrides system locale. 304 * @return A pattern based date/time formatter. 305 * @throws IllegalArgumentException Thrown if pattern is invalid or {@code null}. 306 */ 307 public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone, final Locale locale) { 308 return CACHE.getInstance(pattern, timeZone, locale); 309 } 310 311 /** 312 * Gets a time formatter instance using the specified style in the default time zone and locale. 313 * 314 * @param style time style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}. 315 * @return A localized standard time formatter. 316 * @throws IllegalArgumentException Thrown if the Locale has no time pattern defined. 317 * @since 2.1 318 */ 319 public static FastDateFormat getTimeInstance(final int style) { 320 return CACHE.getTimeInstance(style, null, null); 321 } 322 323 /** 324 * Gets a time formatter instance using the specified style and locale in the default time zone. 325 * 326 * @param style time style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}. 327 * @param locale optional locale, overrides system locale. 328 * @return A localized standard time formatter. 329 * @throws IllegalArgumentException Thrown if the Locale has no time 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 time zone in the default locale. 338 * 339 * @param style time style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}. 340 * @param timeZone optional time zone, overrides time zone of formatted time. 341 * @return A localized standard time formatter. 342 * @throws IllegalArgumentException Thrown if the Locale has no time pattern defined. 343 * @since 2.1 344 */ 345 public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone) { 346 return CACHE.getTimeInstance(style, timeZone, null); 347 } 348 349 /** 350 * Gets a time formatter instance using the specified style, time zone and locale. 351 * 352 * @param style time style: {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, or {@link #SHORT}. 353 * @param timeZone optional time zone, overrides time zone of formatted time. 354 * @param locale optional locale, overrides system locale. 355 * @return A localized standard time formatter. 356 * @throws IllegalArgumentException Thrown if the Locale has no time pattern defined. 357 */ 358 public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone, final Locale locale) { 359 return CACHE.getTimeInstance(style, timeZone, locale); 360 } 361 362 /** Our fast printer. */ 363 private final FastDatePrinter printer; 364 365 /** Our fast parser. */ 366 private final FastDateParser parser; 367 368 /** 369 * Constructs a new FastDateFormat. 370 * 371 * @param pattern {@link java.text.SimpleDateFormat} compatible pattern. 372 * @param timeZone non-null time zone to use. 373 * @param locale non-null locale to use. 374 * @throws NullPointerException Thrown if pattern, timeZone, or locale is null. 375 */ 376 protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale) { 377 this(pattern, timeZone, locale, null); 378 } 379 380 /** 381 * Constructs a new FastDateFormat. 382 * 383 * @param pattern {@link java.text.SimpleDateFormat} compatible pattern. 384 * @param timeZone non-null time zone to use. 385 * @param locale non-null locale to use. 386 * @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 - 387 * 80 years. 388 * @throws NullPointerException Thrown if pattern, timeZone, or locale is null. 389 */ 390 protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale, final Date centuryStart) { 391 printer = new FastDatePrinter(pattern, timeZone, locale); 392 parser = new FastDateParser(pattern, timeZone, locale, centuryStart); 393 } 394 395 /** 396 * Performs the formatting by applying the rules to the specified calendar. 397 * 398 * @param calendar The calendar to format. 399 * @param buf The buffer to format into. 400 * @return The specified string buffer. 401 * @deprecated Use {@link #format(Calendar, Appendable)} 402 */ 403 @Deprecated 404 protected StringBuffer applyRules(final Calendar calendar, final StringBuffer buf) { 405 return printer.format(calendar, buf); 406 } 407 408 /** 409 * Compares two objects for equality. 410 * 411 * @param obj The object to compare to. 412 * @return {@code true} if equal. 413 */ 414 @Override 415 public boolean equals(final Object obj) { 416 if (!(obj instanceof FastDateFormat)) { 417 return false; 418 } 419 final FastDateFormat other = (FastDateFormat) obj; 420 // no need to check parser, as it has same invariants as printer 421 return printer.equals(other.printer); 422 } 423 424 /** 425 * Formats a {@link Calendar} object. 426 * 427 * @param calendar The calendar to format. 428 * @return The formatted string. 429 */ 430 @Override 431 public String format(final Calendar calendar) { 432 return printer.format(calendar); 433 } 434 435 /** 436 * Formats a {@link Calendar} object into the supplied {@link StringBuffer}. 437 * 438 * @param calendar The calendar to format. 439 * @param buf The buffer to format into. 440 * @return The specified string buffer. 441 * @since 3.5 442 */ 443 @Override 444 public <B extends Appendable> B format(final Calendar calendar, final B buf) { 445 return printer.format(calendar, buf); 446 } 447 448 /** 449 * Formats a {@link Calendar} object into the supplied {@link StringBuffer}. 450 * 451 * @param calendar The calendar to format. 452 * @param buf The buffer to format into. 453 * @return The specified string buffer. 454 * @deprecated Use {{@link #format(Calendar, Appendable)}. 455 */ 456 @Deprecated 457 @Override 458 public StringBuffer format(final Calendar calendar, final StringBuffer buf) { 459 return printer.format(calendar, buf); 460 } 461 462 /** 463 * Formats a {@link Date} object using a {@link GregorianCalendar}. 464 * 465 * @param date The date to format. 466 * @return The formatted string. 467 */ 468 @Override 469 public String format(final Date date) { 470 return printer.format(date); 471 } 472 473 /** 474 * Formats a {@link Date} object into the supplied {@link StringBuffer} using a {@link GregorianCalendar}. 475 * 476 * @param date The date to format. 477 * @param buf The buffer to format into. 478 * @return The specified string buffer. 479 * @since 3.5 480 */ 481 @Override 482 public <B extends Appendable> B format(final Date date, final B buf) { 483 return printer.format(date, buf); 484 } 485 486 /** 487 * Formats a {@link Date} object into the supplied {@link StringBuffer} using a {@link GregorianCalendar}. 488 * 489 * @param date The date to format. 490 * @param buf The buffer to format into. 491 * @return The specified string buffer. 492 * @deprecated Use {{@link #format(Date, Appendable)}. 493 */ 494 @Deprecated 495 @Override 496 public StringBuffer format(final Date date, final StringBuffer buf) { 497 return printer.format(date, buf); 498 } 499 500 /** 501 * Formats a millisecond {@code long} value. 502 * 503 * @param millis The millisecond value to format. 504 * @return The formatted string. 505 * @since 2.1 506 */ 507 @Override 508 public String format(final long millis) { 509 return printer.format(millis); 510 } 511 512 /** 513 * Formats a millisecond {@code long} value into the supplied {@link StringBuffer}. 514 * 515 * @param millis The millisecond value to format. 516 * @param buf The buffer to format into. 517 * @return The specified string buffer. 518 * @since 3.5 519 */ 520 @Override 521 public <B extends Appendable> B format(final long millis, final B buf) { 522 return printer.format(millis, buf); 523 } 524 525 /** 526 * Formats a millisecond {@code long} value into the 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 2.1 532 * @deprecated Use {{@link #format(long, Appendable)}. 533 */ 534 @Deprecated 535 @Override 536 public StringBuffer format(final long millis, final StringBuffer buf) { 537 return printer.format(millis, buf); 538 } 539 540 /** 541 * Formats a {@link Date}, {@link Calendar} or {@link Long} (milliseconds) object. This method is an implementation of 542 * {@link Format#format(Object, StringBuffer, FieldPosition)} 543 * 544 * @param obj The object to format. 545 * @param toAppendTo The buffer to append to. 546 * @param pos The position, ignored. 547 * @return The given buffer. 548 */ 549 @Override 550 public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) { 551 return toAppendTo.append(printer.format(obj)); 552 } 553 554 /** 555 * Gets the locale used by this formatter. 556 * 557 * @return The locale. 558 */ 559 @Override 560 public Locale getLocale() { 561 return printer.getLocale(); 562 } 563 564 /** 565 * Gets an estimate for the maximum string length that the formatter will produce. 566 * 567 * <p> 568 * The actual formatted length will almost always be less than or equal to this amount. 569 * </p> 570 * 571 * @return The maximum formatted length. 572 */ 573 public int getMaxLengthEstimate() { 574 return printer.getMaxLengthEstimate(); 575 } 576 577 /** 578 * Gets the pattern used by this formatter. 579 * 580 * @return The pattern, {@link java.text.SimpleDateFormat} compatible. 581 */ 582 @Override 583 public String getPattern() { 584 return printer.getPattern(); 585 } 586 587 /** 588 * Gets the time zone used by this formatter. 589 * 590 * <p> 591 * This zone is always used for {@link Date} formatting. 592 * </p> 593 * 594 * @return A copy of the time zone, changing it has no effect on this formatter. 595 */ 596 @Override 597 public TimeZone getTimeZone() { 598 return printer.getTimeZone(); 599 } 600 601 /** 602 * Returns a hash code compatible with equals. 603 * 604 * @return A hash code compatible with equals. 605 */ 606 @Override 607 public int hashCode() { 608 return printer.hashCode(); 609 } 610 611 /* 612 * (non-Javadoc) 613 * 614 * @see DateParser#parse(String) 615 */ 616 @Override 617 public Date parse(final String source) throws ParseException { 618 return parser.parse(source); 619 } 620 621 /* 622 * (non-Javadoc) 623 * 624 * @see DateParser#parse(String, java.text.ParsePosition) 625 */ 626 @Override 627 public Date parse(final String source, final ParsePosition pos) { 628 return parser.parse(source, pos); 629 } 630 631 /* 632 * (non-Javadoc) 633 * 634 * @see org.apache.commons.lang3.time.DateParser#parse(String, java.text.ParsePosition, java.util.Calendar) 635 */ 636 @Override 637 public boolean parse(final String source, final ParsePosition pos, final Calendar calendar) { 638 return parser.parse(source, pos, calendar); 639 } 640 641 /* 642 * (non-Javadoc) 643 * 644 * @see java.text.Format#parseObject(String, java.text.ParsePosition) 645 */ 646 @Override 647 public Object parseObject(final String source, final ParsePosition pos) { 648 return parser.parseObject(source, pos); 649 } 650 651 /** 652 * Gets a debugging string version of this formatter. 653 * 654 * @return A debug string. 655 */ 656 @Override 657 public String toString() { 658 return "FastDateFormat[" + printer.getPattern() + "," + printer.getLocale() + "," + printer.getTimeZone().getID() + "]"; 659 } 660}