1 // 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html#License 3 /* 4 ******************************************************************************* 5 * Copyright (C) 1996-2012, International Business Machines Corporation and * 6 * others. All Rights Reserved. * 7 ******************************************************************************* 8 */ 9 10 package com.ibm.icu.text; 11 12 import java.text.AttributedCharacterIterator; 13 import java.text.AttributedCharacterIterator.Attribute; 14 import java.text.AttributedString; 15 import java.text.CharacterIterator; 16 import java.text.FieldPosition; 17 import java.text.ParsePosition; 18 import java.util.Date; 19 import java.util.Locale; 20 import java.util.Map; 21 import java.util.Map.Entry; 22 23 import com.ibm.icu.util.Calendar; 24 import com.ibm.icu.util.ULocale; 25 26 27 /** 28 * {@icuenhanced java.text.SimpleDateFormat}.{@icu _usage_} 29 * 30 * <p><code>SimpleDateFormat</code> is a concrete class for formatting and 31 * parsing dates in a locale-sensitive manner. It allows for formatting 32 * (date -> text), parsing (text -> date), and normalization. 33 * 34 * <p> 35 * <code>SimpleDateFormat</code> allows you to start by choosing 36 * any user-defined patterns for date-time formatting. However, you 37 * are encouraged to create a date-time formatter with either 38 * <code>getTimeInstance</code>, <code>getDateInstance</code>, or 39 * <code>getDateTimeInstance</code> in <code>DateFormat</code>. Each 40 * of these class methods can return a date/time formatter initialized 41 * with a default format pattern. You may modify the format pattern 42 * using the <code>applyPattern</code> methods as desired. 43 * For more information on using these methods, see 44 * {@link DateFormat}. 45 * 46 * <p> 47 * <strong>Time Format Syntax:</strong> 48 * <p> 49 * To specify the time format use a <em>time pattern</em> string. 50 * In this pattern, all ASCII letters are reserved as pattern letters, 51 * which are defined as the following: 52 * <blockquote> 53 * <pre> 54 * Symbol Meaning Presentation Example 55 * ------ ------- ------------ ------- 56 * G era designator (Text) AD 57 * y† year (Number) 1996 58 * Y* year (week of year) (Number) 1997 59 * u* extended year (Number) 4601 60 * M month in year (Text & Number) July & 07 61 * d day in month (Number) 10 62 * h hour in am/pm (1~12) (Number) 12 63 * H hour in day (0~23) (Number) 0 64 * m minute in hour (Number) 30 65 * s second in minute (Number) 55 66 * S fractional second (Number) 978 67 * E day of week (Text) Tuesday 68 * e* day of week (local 1~7) (Text & Number) Tuesday & 2 69 * D day in year (Number) 189 70 * F day of week in month (Number) 2 (2nd Wed in July) 71 * w week in year (Number) 27 72 * W week in month (Number) 2 73 * a am/pm marker (Text) PM 74 * k hour in day (1~24) (Number) 24 75 * K hour in am/pm (0~11) (Number) 0 76 * z time zone (Text) Pacific Standard Time 77 * Z time zone (RFC 822) (Number) -0800 78 * v time zone (generic) (Text) Pacific Time 79 * V time zone (location) (Text) United States (Los Angeles) 80 * g* Julian day (Number) 2451334 81 * A* milliseconds in day (Number) 69540000 82 * Q* quarter in year (Text & Number) Q1 & 01 83 * c* stand alone day of week (Text & Number) Tuesday & 2 84 * L* stand alone month (Text & Number) July & 07 85 * q* stand alone quarter (Text & Number) Q1 & 01 86 * ' escape for text (Delimiter) 'Date=' 87 * '' single quote (Literal) 'o''clock' 88 * </pre> 89 * </blockquote> 90 * <tt><b>*</b></tt> These items are not supported by Java's SimpleDateFormat.<br> 91 * <tt><b>†</b></tt> ICU interprets a single 'y' differently than Java.</p> 92 * <p> 93 * The count of pattern letters determine the format. 94 * <p> 95 * <strong>(Text)</strong>: 4 or more pattern letters--use full form, 96 * < 4--use short or abbreviated form if one exists. 97 * <p> 98 * <strong>(Number)</strong>: the minimum number of digits. Shorter 99 * numbers are zero-padded to this amount. Year is handled specially; 100 * that is, if the count of 'y' is 2, the Year will be truncated to 2 digits. 101 * (e.g., if "yyyy" produces "1997", "yy" produces "97".) 102 * Unlike other fields, fractional seconds are padded on the right with zero. 103 * <p> 104 * <strong>(Text & Number)</strong>: 3 or over, use text, otherwise use number. 105 * <p> 106 * Any characters in the pattern that are not in the ranges of ['a'..'z'] 107 * and ['A'..'Z'] will be treated as quoted text. For instance, characters 108 * like ':', '.', ' ', '#' and '@' will appear in the resulting time text 109 * even they are not embraced within single quotes. 110 * <p> 111 * A pattern containing any invalid pattern letter will result in a thrown 112 * exception during formatting or parsing. 113 * 114 * <p> 115 * <strong>Examples Using the US Locale:</strong> 116 * <blockquote> 117 * <pre> 118 * Format Pattern Result 119 * -------------- ------- 120 * "yyyy.MM.dd G 'at' HH:mm:ss vvvv" ->> 1996.07.10 AD at 15:08:56 Pacific Time 121 * "EEE, MMM d, ''yy" ->> Wed, July 10, '96 122 * "h:mm a" ->> 12:08 PM 123 * "hh 'o''clock' a, zzzz" ->> 12 o'clock PM, Pacific Daylight Time 124 * "K:mm a, vvv" ->> 0:00 PM, PT 125 * "yyyyy.MMMMM.dd GGG hh:mm aaa" ->> 01996.July.10 AD 12:08 PM 126 * </pre> 127 * </blockquote> 128 * <strong>Code Sample:</strong> 129 * <blockquote> 130 * <pre> 131 * SimpleTimeZone pdt = new SimpleTimeZone(-8 * 60 * 60 * 1000, "PST"); 132 * pdt.setStartRule(Calendar.APRIL, 1, Calendar.SUNDAY, 2*60*60*1000); 133 * pdt.setEndRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2*60*60*1000); 134 * <br> 135 * // Format the current time. 136 * SimpleDateFormat formatter 137 * = new SimpleDateFormat ("yyyy.MM.dd G 'at' hh:mm:ss a zzz"); 138 * Date currentTime_1 = new Date(); 139 * String dateString = formatter.format(currentTime_1); 140 * <br> 141 * // Parse the previous string back into a Date. 142 * ParsePosition pos = new ParsePosition(0); 143 * Date currentTime_2 = formatter.parse(dateString, pos); 144 * </pre> 145 * </blockquote> 146 * In the example, the time value <code>currentTime_2</code> obtained from 147 * parsing will be equal to <code>currentTime_1</code>. However, they may not be 148 * equal if the am/pm marker 'a' is left out from the format pattern while 149 * the "hour in am/pm" pattern symbol is used. This information loss can 150 * happen when formatting the time in PM. 151 * 152 * <p>When parsing a date string using the abbreviated year pattern ("yy"), 153 * SimpleDateFormat must interpret the abbreviated year 154 * relative to some century. It does this by adjusting dates to be 155 * within 80 years before and 20 years after the time the SimpleDateFormat 156 * instance is created. For example, using a pattern of "MM/dd/yy" and a 157 * SimpleDateFormat instance created on Jan 1, 1997, the string 158 * "01/11/12" would be interpreted as Jan 11, 2012 while the string "05/04/64" 159 * would be interpreted as May 4, 1964. 160 * During parsing, only strings consisting of exactly two digits, as defined by 161 * {@link com.ibm.icu.lang.UCharacter#isDigit(int)}, will be parsed into the default 162 * century. 163 * Any other numeric string, such as a one digit string, a three or more digit 164 * string, or a two digit string that isn't all digits (for example, "-1"), is 165 * interpreted literally. So "01/02/3" or "01/02/003" are parsed, using the 166 * same pattern, as Jan 2, 3 AD. Likewise, "01/02/-3" is parsed as Jan 2, 4 BC. 167 * 168 * <p>If the year pattern does not have exactly two 'y' characters, the year is 169 * interpreted literally, regardless of the number of digits. So using the 170 * pattern "MM/dd/yyyy", "01/11/12" parses to Jan 11, 12 A.D. 171 * 172 * <p>When numeric fields abut one another directly, with no intervening delimiter 173 * characters, they constitute a run of abutting numeric fields. Such runs are 174 * parsed specially. For example, the format "HHmmss" parses the input text 175 * "123456" to 12:34:56, parses the input text "12345" to 1:23:45, and fails to 176 * parse "1234". In other words, the leftmost field of the run is flexible, 177 * while the others keep a fixed width. If the parse fails anywhere in the run, 178 * then the leftmost field is shortened by one character, and the entire run is 179 * parsed again. This is repeated until either the parse succeeds or the 180 * leftmost field is one character in length. If the parse still fails at that 181 * point, the parse of the run fails. 182 * 183 * <p>For time zones that have no names, use strings GMT+hours:minutes or 184 * GMT-hours:minutes. 185 * 186 * <p>The calendar defines what is the first day of the week, the first week 187 * of the year, whether hours are zero based or not (0 vs 12 or 24), and the 188 * time zone. There is one common decimal format to handle all the numbers; 189 * the digit count is handled programmatically according to the pattern. 190 * 191 * <h4>Synchronization</h4> 192 * 193 * Date formats are not synchronized. It is recommended to create separate 194 * format instances for each thread. If multiple threads access a format 195 * concurrently, it must be synchronized externally. 196 * 197 * @see com.ibm.icu.util.Calendar 198 * @see com.ibm.icu.util.GregorianCalendar 199 * @see com.ibm.icu.util.TimeZone 200 * @see DateFormat 201 * @see DateFormatSymbols 202 * @see DecimalFormat 203 * @author Mark Davis, Chen-Lieh Huang, Alan Liu 204 * @stable ICU 2.0 205 */ 206 public class SimpleDateFormat extends DateFormat { 207 private static final long serialVersionUID = 1L; 208 209 /** 210 * Constructs a SimpleDateFormat using the default pattern for the default 211 * locale. <b>Note:</b> Not all locales support SimpleDateFormat; for full 212 * generality, use the factory methods in the DateFormat class. 213 * 214 * @see DateFormat 215 * @stable ICU 2.0 216 */ 217 public SimpleDateFormat() { 218 super(new java.text.SimpleDateFormat()); 219 } 220 221 /** 222 * Constructs a SimpleDateFormat using the given pattern in the default 223 * locale. <b>Note:</b> Not all locales support SimpleDateFormat; for full 224 * generality, use the factory methods in the DateFormat class. 225 * @stable ICU 2.0 226 */ 227 public SimpleDateFormat(String pattern) 228 { 229 super(new java.text.SimpleDateFormat(pattern)); 230 } 231 232 /** 233 * Constructs a SimpleDateFormat using the given pattern and locale. 234 * <b>Note:</b> Not all locales support SimpleDateFormat; for full 235 * generality, use the factory methods in the DateFormat class. 236 * @stable ICU 2.0 237 */ 238 public SimpleDateFormat(String pattern, Locale loc) 239 { 240 super(new java.text.SimpleDateFormat(pattern, loc)); 241 } 242 243 /** 244 * Constructs a SimpleDateFormat using the given pattern and locale. 245 * <b>Note:</b> Not all locales support SimpleDateFormat; for full 246 * generality, use the factory methods in the DateFormat class. 247 * @stable ICU 3.2 248 */ 249 public SimpleDateFormat(String pattern, ULocale loc) 250 { 251 this(pattern, loc.toLocale()); 252 } 253 254 // /** 255 // * Constructs a SimpleDateFormat using the given pattern , override and locale. 256 // * @param pattern The pattern to be used 257 // * @param override The override string. A numbering system override string can take one of the following forms: 258 // * 1). If just a numbering system name is specified, it applies to all numeric fields in the date format pattern. 259 // * 2). To specify an alternate numbering system on a field by field basis, use the field letters from the pattern 260 // * followed by an = sign, followed by the numbering system name. For example, to specify that just the year 261 // * be formatted using Hebrew digits, use the override "y=hebr". Multiple overrides can be specified in a single 262 // * string by separating them with a semi-colon. For example, the override string "m=thai;y=deva" would format using 263 // * Thai digits for the month and Devanagari digits for the year. 264 // * @param loc The locale to be used 265 // * @stable ICU 4.2 266 // */ 267 // public SimpleDateFormat(String pattern, String override, ULocale loc) 268 // { 269 // throw new UnsupportedOperationException("Method not supported by com.ibm.icu.base"); 270 // } 271 272 /** 273 * Constructs a SimpleDateFormat using the given pattern and 274 * locale-specific symbol data. 275 * Warning: uses default locale for digits! 276 * @stable ICU 2.0 277 */ 278 public SimpleDateFormat(String pattern, DateFormatSymbols formatData) 279 { 280 super(new java.text.SimpleDateFormat(pattern, formatData.dfs)); 281 } 282 283 /** 284 * Sets the 100-year period 2-digit years will be interpreted as being in 285 * to begin on the date the user specifies. 286 * @param startDate During parsing, two digit years will be placed in the range 287 * <code>startDate</code> to <code>startDate + 100 years</code>. 288 * @stable ICU 2.0 289 */ 290 public void set2DigitYearStart(Date startDate) { 291 ((java.text.SimpleDateFormat)dateFormat).set2DigitYearStart(startDate); 292 } 293 294 /** 295 * Returns the beginning date of the 100-year period 2-digit years are interpreted 296 * as being within. 297 * @return the start of the 100-year period into which two digit years are 298 * parsed 299 * @stable ICU 2.0 300 */ 301 public Date get2DigitYearStart() { 302 return ((java.text.SimpleDateFormat)dateFormat).get2DigitYearStart(); 303 } 304 305 /** 306 * Formats a date or time, which is the standard millis 307 * since January 1, 1970, 00:00:00 GMT. 308 * <p>Example: using the US locale: 309 * "yyyy.MM.dd G 'at' HH:mm:ss zzz" ->> 1996.07.10 AD at 15:08:56 PDT 310 * @param cal the calendar whose date-time value is to be formatted into a date-time string 311 * @param toAppendTo where the new date-time text is to be appended 312 * @param pos the formatting position. On input: an alignment field, 313 * if desired. On output: the offsets of the alignment field. 314 * @return the formatted date-time string. 315 * @see DateFormat 316 * @stable ICU 2.0 317 */ 318 public StringBuffer format(Calendar cal, StringBuffer toAppendTo, 319 FieldPosition pos) { 320 StringBuffer result; 321 FieldPosition jdkPos = toJDKFieldPosition(pos); 322 synchronized(dateFormat) { 323 java.util.Calendar oldCal = dateFormat.getCalendar(); 324 dateFormat.setCalendar(cal.calendar); 325 result = dateFormat.format(cal.getTime(), toAppendTo, jdkPos); 326 dateFormat.setCalendar(oldCal); 327 } 328 if (jdkPos != null) { 329 pos.setBeginIndex(jdkPos.getBeginIndex()); 330 pos.setEndIndex(jdkPos.getEndIndex()); 331 } 332 return result; 333 } 334 335 /** 336 * Overrides superclass method 337 * @stable ICU 2.0 338 */ 339 public void setNumberFormat(NumberFormat newNumberFormat) { 340 super.setNumberFormat(newNumberFormat); 341 } 342 343 /** 344 * Overrides DateFormat 345 * @see DateFormat 346 * @stable ICU 2.0 347 */ 348 public void parse(String text, Calendar cal, ParsePosition parsePos) 349 { 350 // Note: parsed time zone won't be set in the result calendar 351 cal.setTime(dateFormat.parse(text, parsePos)); 352 } 353 354 /** 355 * Return a pattern string describing this date format. 356 * @stable ICU 2.0 357 */ 358 public String toPattern() { 359 return ((java.text.SimpleDateFormat)dateFormat).toPattern(); 360 } 361 362 /** 363 * Return a localized pattern string describing this date format. 364 * @stable ICU 2.0 365 */ 366 public String toLocalizedPattern() { 367 return ((java.text.SimpleDateFormat)dateFormat).toLocalizedPattern(); 368 } 369 370 /** 371 * Apply the given unlocalized pattern string to this date format. 372 * @stable ICU 2.0 373 */ 374 public void applyPattern(String pat) { 375 ((java.text.SimpleDateFormat)dateFormat).applyPattern(pat); 376 } 377 378 /** 379 * Apply the given localized pattern string to this date format. 380 * @stable ICU 2.0 381 */ 382 public void applyLocalizedPattern(String pat) { 383 ((java.text.SimpleDateFormat)dateFormat).applyLocalizedPattern(pat); 384 } 385 386 /** 387 * Gets the date/time formatting data. 388 * @return a copy of the date-time formatting data associated 389 * with this date-time formatter. 390 * @stable ICU 2.0 391 */ 392 public DateFormatSymbols getDateFormatSymbols() { 393 return new DateFormatSymbols(((java.text.SimpleDateFormat)dateFormat).getDateFormatSymbols()); 394 } 395 396 /** 397 * Allows you to set the date/time formatting data. 398 * @param newFormatSymbols the new symbols 399 * @stable ICU 2.0 400 */ 401 public void setDateFormatSymbols(DateFormatSymbols newFormatSymbols) { 402 ((java.text.SimpleDateFormat)dateFormat).setDateFormatSymbols(newFormatSymbols.dfs); 403 } 404 405 // /** 406 // * {@icu} Gets the time zone formatter which this date/time 407 // * formatter uses to format and parse a time zone. 408 // * 409 // * @return the time zone formatter which this date/time 410 // * formatter uses. 411 // * @draft ICU 49 412 // * @provisional This API might change or be removed in a future release. 413 // */ 414 // public TimeZoneFormat getTimeZoneFormat() { 415 // throw new UnsupportedOperationException("Method not supported by com.ibm.icu.base"); 416 // } 417 418 // /** 419 // * {@icu} Allows you to set the time zone formatter. 420 // * 421 // * @param tzfmt the new time zone formatter 422 // * @draft ICU 49 423 // * @provisional This API might change or be removed in a future release. 424 // */ 425 // public void setTimeZoneFormat(TimeZoneFormat tzfmt) { 426 // throw new UnsupportedOperationException("Method not supported by com.ibm.icu.base"); 427 // } 428 429 // For clone to use 430 private SimpleDateFormat(java.text.SimpleDateFormat sdf) { 431 super(sdf); 432 } 433 434 /** 435 * Overrides Cloneable 436 * @stable ICU 2.0 437 */ 438 public Object clone() { 439 return new SimpleDateFormat((java.text.SimpleDateFormat)dateFormat.clone()); 440 } 441 442 /** 443 * Override hashCode. 444 * Generates the hash code for the SimpleDateFormat object 445 * @stable ICU 2.0 446 */ 447 public int hashCode() 448 { 449 return super.hashCode(); 450 } 451 452 /** 453 * Override equals. 454 * @stable ICU 2.0 455 */ 456 public boolean equals(Object obj) 457 { 458 return super.equals(obj); 459 } 460 461 /** 462 * Format the object to an attributed string, and return the corresponding iterator 463 * Overrides superclass method. 464 * 465 * @param obj The object to format 466 * @return <code>AttributedCharacterIterator</code> describing the formatted value. 467 * 468 * @stable ICU 3.8 469 */ 470 public AttributedCharacterIterator formatToCharacterIterator(Object obj) { 471 AttributedCharacterIterator it = dateFormat.formatToCharacterIterator(obj); 472 473 // Extract formatted String first 474 StringBuilder sb = new StringBuilder(); 475 for (char c = it.first(); c != CharacterIterator.DONE; c = it.next()) { 476 sb.append(c); 477 } 478 479 // Create AttributedString 480 AttributedString attrstr = new AttributedString(sb.toString()); 481 482 // Map JDK Field to ICU Field 483 int idx = 0; 484 it.first(); 485 while (idx < it.getEndIndex()) { 486 int end = it.getRunLimit(); 487 Map<Attribute, Object> attributes = it.getAttributes(); 488 if (attributes != null) { 489 for (Entry<Attribute, Object> entry : attributes.entrySet()) { 490 Attribute attr = entry.getKey(); 491 Object val = entry.getValue(); 492 if (attr.equals(java.text.DateFormat.Field.AM_PM)) { 493 val = attr = Field.AM_PM; 494 } else if (attr.equals(java.text.DateFormat.Field.DAY_OF_MONTH)) { 495 val = attr = Field.DAY_OF_MONTH; 496 } else if (attr.equals(java.text.DateFormat.Field.DAY_OF_WEEK)) { 497 val = attr = Field.DAY_OF_WEEK ; 498 } else if (attr.equals(java.text.DateFormat.Field.DAY_OF_WEEK_IN_MONTH)) { 499 val = attr = Field.DAY_OF_WEEK_IN_MONTH ; 500 } else if (attr.equals(java.text.DateFormat.Field.DAY_OF_YEAR)) { 501 val = attr = Field.DAY_OF_YEAR; 502 } else if (attr.equals(java.text.DateFormat.Field.ERA)) { 503 val = attr = Field.ERA; 504 } else if (attr.equals(java.text.DateFormat.Field.HOUR_OF_DAY0)) { 505 val = attr = Field.HOUR_OF_DAY0; 506 } else if (attr.equals(java.text.DateFormat.Field.HOUR_OF_DAY1)) { 507 val = attr = Field.HOUR_OF_DAY1; 508 } else if (attr.equals(java.text.DateFormat.Field.HOUR0)) { 509 val = attr = Field.HOUR0; 510 } else if (attr.equals(java.text.DateFormat.Field.HOUR1)) { 511 val = attr = Field.HOUR1; 512 } else if (attr.equals(java.text.DateFormat.Field.MILLISECOND)) { 513 val = attr = Field.MILLISECOND; 514 } else if (attr.equals(java.text.DateFormat.Field.MINUTE)) { 515 val = attr = Field.MINUTE; 516 } else if (attr.equals(java.text.DateFormat.Field.MONTH)) { 517 val = attr = Field.MONTH; 518 } else if (attr.equals(java.text.DateFormat.Field.SECOND)) { 519 val = attr = Field.SECOND; 520 } else if (attr.equals(java.text.DateFormat.Field.TIME_ZONE)) { 521 val = attr = Field.TIME_ZONE; 522 } else if (attr.equals(java.text.DateFormat.Field.WEEK_OF_MONTH)) { 523 val = attr = Field.WEEK_OF_MONTH; 524 } else if (attr.equals(java.text.DateFormat.Field.WEEK_OF_YEAR)) { 525 val = attr = Field.WEEK_OF_YEAR; 526 } else if (attr.equals(java.text.DateFormat.Field.YEAR)) { 527 val = attr = Field.YEAR; 528 } 529 attrstr.addAttribute(attr, val, idx, end); 530 } 531 } 532 idx = end; 533 while (it.getIndex() < idx) { 534 it.next(); 535 } 536 } 537 538 return attrstr.getIterator(); 539 } 540 } 541