DateIntervalFormat.java revision 08ae9f2909b2ec37f755dac4372553437e9d7cf6
1/* GENERATED SOURCE. DO NOT MODIFY. */ 2/* 3* Copyright (C) 2008-2015, International Business Machines 4* Corporation and others. All Rights Reserved. 5*/ 6 7package android.icu.text; 8 9import java.io.IOException; 10import java.io.ObjectInputStream; 11import java.text.FieldPosition; 12import java.text.ParsePosition; 13import java.util.Collections; 14import java.util.HashMap; 15import java.util.Locale; 16import java.util.Map; 17 18import android.icu.impl.CalendarData; 19import android.icu.impl.ICUCache; 20import android.icu.impl.SimpleCache; 21import android.icu.text.DateIntervalInfo.PatternInfo; 22import android.icu.util.Calendar; 23import android.icu.util.DateInterval; 24import android.icu.util.Output; 25import android.icu.util.TimeZone; 26import android.icu.util.ULocale; 27import android.icu.util.ULocale.Category; 28 29 30/** 31 * DateIntervalFormat is a class for formatting and parsing date 32 * intervals in a language-independent manner. 33 * Only formatting is supported. Parsing is not supported. 34 * 35 * <P> 36 * Date interval means from one date to another date, 37 * for example, from "Jan 11, 2008" to "Jan 18, 2008". 38 * We introduced class DateInterval to represent it. 39 * DateInterval is a pair of UDate, which is 40 * the standard milliseconds since 24:00 GMT, Jan 1, 1970. 41 * 42 * <P> 43 * DateIntervalFormat formats a DateInterval into 44 * text as compactly as possible. 45 * For example, the date interval format from "Jan 11, 2008" to "Jan 18,. 2008" 46 * is "Jan 11-18, 2008" for English. 47 * And it parses text into DateInterval, 48 * although initially, parsing is not supported. 49 * 50 * <P> 51 * There is no structural information in date time patterns. 52 * For any punctuations and string literals inside a date time pattern, 53 * we do not know whether it is just a separator, or a prefix, or a suffix. 54 * Without such information, so, it is difficult to generate a sub-pattern 55 * (or super-pattern) by algorithm. 56 * So, formatting a DateInterval is pattern-driven. It is very 57 * similar to formatting in SimpleDateFormat. 58 * We introduce class DateIntervalInfo to save date interval 59 * patterns, similar to date time pattern in SimpleDateFormat. 60 * 61 * <P> 62 * Logically, the interval patterns are mappings 63 * from (skeleton, the_largest_different_calendar_field) 64 * to (date_interval_pattern). 65 * 66 * <P> 67 * A skeleton 68 * <ol> 69 * <li> 70 * only keeps the field pattern letter and ignores all other parts 71 * in a pattern, such as space, punctuations, and string literals. 72 * <li> 73 * hides the order of fields. 74 * <li> 75 * might hide a field's pattern letter length. 76 * 77 * For those non-digit calendar fields, the pattern letter length is 78 * important, such as MMM, MMMM, and MMMMM; EEE and EEEE, 79 * and the field's pattern letter length is honored. 80 * 81 * For the digit calendar fields, such as M or MM, d or dd, yy or yyyy, 82 * the field pattern length is ignored and the best match, which is defined 83 * in date time patterns, will be returned without honor the field pattern 84 * letter length in skeleton. 85 * </ol> 86 * 87 * <P> 88 * The calendar fields we support for interval formatting are: 89 * year, month, date, day-of-week, am-pm, hour, hour-of-day, minute, and 90 * second (though we do not currently have specific intervalFormat data for 91 * skeletons with seconds). 92 * Those calendar fields can be defined in the following order: 93 * year > month > date > hour (in day) > minute > second 94 * 95 * The largest different calendar fields between 2 calendars is the 96 * first different calendar field in above order. 97 * 98 * For example: the largest different calendar fields between "Jan 10, 2007" 99 * and "Feb 20, 2008" is year. 100 * 101 * <P> 102 * For other calendar fields, the compact interval formatting is not 103 * supported. And the interval format will be fall back to fall-back 104 * patterns, which is mostly "{date0} - {date1}". 105 * 106 * <P> 107 * There is a set of pre-defined static skeleton strings in DateFormat, 108 * There are pre-defined interval patterns for those pre-defined skeletons 109 * in locales' resource files. 110 * For example, for a skeleton YEAR_ABBR_MONTH_DAY, which is "yMMMd", 111 * in en_US, if the largest different calendar field between date1 and date2 112 * is "year", the date interval pattern is "MMM d, yyyy - MMM d, yyyy", 113 * such as "Jan 10, 2007 - Jan 10, 2008". 114 * If the largest different calendar field between date1 and date2 is "month", 115 * the date interval pattern is "MMM d - MMM d, yyyy", 116 * such as "Jan 10 - Feb 10, 2007". 117 * If the largest different calendar field between date1 and date2 is "day", 118 * the date interval pattern is ""MMM d-d, yyyy", such as "Jan 10-20, 2007". 119 * 120 * For date skeleton, the interval patterns when year, or month, or date is 121 * different are defined in resource files. 122 * For time skeleton, the interval patterns when am/pm, or hour, or minute is 123 * different are defined in resource files. 124 * 125 * <P> 126 * If a skeleton is not found in a locale's DateIntervalInfo, which means 127 * the interval patterns for the skeleton is not defined in resource file, 128 * the interval pattern will falls back to the interval "fallback" pattern 129 * defined in resource file. 130 * If the interval "fallback" pattern is not defined, the default fall-back 131 * is "{date0} - {data1}". 132 * 133 * <P> 134 * For the combination of date and time, 135 * The rule to genearte interval patterns are: 136 * <ol> 137 * <li> 138 * when the year, month, or day differs, falls back to fall-back 139 * interval pattern, which mostly is the concatenate the two original 140 * expressions with a separator between, 141 * For example, interval pattern from "Jan 10, 2007 10:10 am" 142 * to "Jan 11, 2007 10:10am" is 143 * "Jan 10, 2007 10:10 am - Jan 11, 2007 10:10am" 144 * <li> 145 * otherwise, present the date followed by the range expression 146 * for the time. 147 * For example, interval pattern from "Jan 10, 2007 10:10 am" 148 * to "Jan 10, 2007 11:10am" is "Jan 10, 2007 10:10 am - 11:10am" 149 * </ol> 150 * 151 * 152 * <P> 153 * If two dates are the same, the interval pattern is the single date pattern. 154 * For example, interval pattern from "Jan 10, 2007" to "Jan 10, 2007" is 155 * "Jan 10, 2007". 156 * 157 * Or if the presenting fields between 2 dates have the exact same values, 158 * the interval pattern is the single date pattern. 159 * For example, if user only requests year and month, 160 * the interval pattern from "Jan 10, 2007" to "Jan 20, 2007" is "Jan 2007". 161 * 162 * <P> 163 * DateIntervalFormat needs the following information for correct 164 * formatting: time zone, calendar type, pattern, date format symbols, 165 * and date interval patterns. 166 * It can be instantiated in several ways: 167 * <ol> 168 * <li> 169 * create an instance using default or given locale plus given skeleton. 170 * Users are encouraged to created date interval formatter this way and 171 * to use the pre-defined skeleton macros, such as 172 * YEAR_NUM_MONTH, which consists the calendar fields and 173 * the format style. 174 * </li> 175 * <li> 176 * create an instance using default or given locale plus given skeleton 177 * plus a given DateIntervalInfo. 178 * This factory method is for powerful users who want to provide their own 179 * interval patterns. 180 * Locale provides the timezone, calendar, and format symbols information. 181 * Local plus skeleton provides full pattern information. 182 * DateIntervalInfo provides the date interval patterns. 183 * </li> 184 * </ol> 185 * 186 * <P> 187 * For the calendar field pattern letter, such as G, y, M, d, a, h, H, m, s etc. 188 * DateIntervalFormat uses the same syntax as that of 189 * DateTime format. 190 * 191 * <P> 192 * Code Sample: general usage 193 * <pre> 194 * 195 * // the date interval object which the DateIntervalFormat formats on 196 * // and parses into 197 * DateInterval dtInterval = new DateInterval(1000*3600*24L, 1000*3600*24*2L); 198 * DateIntervalFormat dtIntervalFmt = DateIntervalFormat.getInstance( 199 * YEAR_MONTH_DAY, Locale("en", "GB", "")); 200 * StringBuffer str = new StringBuffer(""); 201 * FieldPosition pos = new FieldPosition(0); 202 * // formatting 203 * dtIntervalFmt.format(dtInterval, dateIntervalString, pos); 204 * 205 * </pre> 206 * 207 * <P> 208 * Code Sample: for powerful users who wants to use their own interval pattern 209 * <pre> 210 * 211 * import android.icu.text.DateIntervalInfo; 212 * import android.icu.text.DateIntervalFormat; 213 * .................... 214 * 215 * // Get DateIntervalFormat instance using default locale 216 * DateIntervalFormat dtitvfmt = DateIntervalFormat.getInstance(YEAR_MONTH_DAY); 217 * 218 * // Create an empty DateIntervalInfo object, which does not have any interval patterns inside. 219 * dtitvinf = new DateIntervalInfo(); 220 * 221 * // a series of set interval patterns. 222 * // Only ERA, YEAR, MONTH, DATE, DAY_OF_MONTH, DAY_OF_WEEK, AM_PM, HOUR, HOUR_OF_DAY, 223 * MINUTE and SECOND are supported. 224 * dtitvinf.setIntervalPattern("yMMMd", Calendar.YEAR, "'y ~ y'"); 225 * dtitvinf.setIntervalPattern("yMMMd", Calendar.MONTH, "yyyy 'diff' MMM d - MMM d"); 226 * dtitvinf.setIntervalPattern("yMMMd", Calendar.DATE, "yyyy MMM d ~ d"); 227 * dtitvinf.setIntervalPattern("yMMMd", Calendar.HOUR_OF_DAY, "yyyy MMM d HH:mm ~ HH:mm"); 228 * 229 * // Set fallback interval pattern. Fallback pattern is used when interval pattern is not found. 230 * // If the fall-back pattern is not set, falls back to {date0} - {date1} if interval pattern is not found. 231 * dtitvinf.setFallbackIntervalPattern("{0} - {1}"); 232 * 233 * // Set above DateIntervalInfo object as the interval patterns of date interval formatter 234 * dtitvfmt.setDateIntervalInfo(dtitvinf); 235 * 236 * // Prepare to format 237 * pos = new FieldPosition(0); 238 * str = new StringBuffer(""); 239 * 240 * // The 2 calendars should be equivalent, otherwise, IllegalArgumentException will be thrown by format() 241 * Calendar fromCalendar = (Calendar) dtfmt.getCalendar().clone(); 242 * Calendar toCalendar = (Calendar) dtfmt.getCalendar().clone(); 243 * fromCalendar.setTimeInMillis(....); 244 * toCalendar.setTimeInMillis(...); 245 * 246 * //Formatting given 2 calendars 247 * dtitvfmt.format(fromCalendar, toCalendar, str, pos); 248 * 249 * 250 * </pre> 251 */ 252 253public class DateIntervalFormat extends UFormat { 254 255 private static final long serialVersionUID = 1; 256 257 /** 258 * Used to save the information for a skeleton's best match skeleton. 259 * It is package accessible since it is used in DateIntervalInfo too. 260 */ 261 static final class BestMatchInfo { 262 // the best match skeleton 263 final String bestMatchSkeleton; 264 // 0 means the best matched skeleton is the same as input skeleton 265 // 1 means the fields are the same, but field width are different 266 // 2 means the only difference between fields are v/z, 267 // -1 means there are other fields difference 268 final int bestMatchDistanceInfo; 269 BestMatchInfo(String bestSkeleton, int difference) { 270 bestMatchSkeleton = bestSkeleton; 271 bestMatchDistanceInfo = difference; 272 } 273 } 274 275 276 /* 277 * Used to save the information on a skeleton and its best match. 278 */ 279 private static final class SkeletonAndItsBestMatch { 280 final String skeleton; 281 final String bestMatchSkeleton; 282 SkeletonAndItsBestMatch(String skeleton, String bestMatch) { 283 this.skeleton = skeleton; 284 bestMatchSkeleton = bestMatch; 285 } 286 } 287 288 289 // Cache for the locale interval pattern 290 private static ICUCache<String, Map<String, PatternInfo>> LOCAL_PATTERN_CACHE = 291 new SimpleCache<String, Map<String, PatternInfo>>(); 292 293 /* 294 * The interval patterns for this locale. 295 */ 296 private DateIntervalInfo fInfo; 297 298 /* 299 * The DateFormat object used to format single pattern 300 */ 301 private SimpleDateFormat fDateFormat; 302 303 /* 304 * The 2 calendars with the from and to date. 305 * could re-use the calendar in fDateFormat, 306 * but keeping 2 calendars make it clear and clean. 307 */ 308 private Calendar fFromCalendar; 309 private Calendar fToCalendar; 310 311 /* 312 * Following are transient interval information 313 * relevant (locale) to this formatter. 314 */ 315 private String fSkeleton = null; 316 317 /* 318 * Needed for efficient deserialization. If set, it means we can use the 319 * cache to initialize fIntervalPatterns. 320 */ 321 private boolean isDateIntervalInfoDefault; 322 323 /** 324 * Interval patterns for this instance's locale. 325 */ 326 private transient Map<String, PatternInfo> fIntervalPatterns = null; 327 328 /* 329 * Patterns for fallback formatting. 330 */ 331 private String fDatePattern = null; 332 private String fTimePattern = null; 333 private String fDateTimeFormat = null; 334 335 336 /* 337 * default constructor; private because we don't want anyone to use 338 */ 339 @SuppressWarnings("unused") 340 private DateIntervalFormat() { 341 } 342 343 /** 344 * Construct a DateIntervalFormat from DateFormat, 345 * a DateIntervalInfo, and skeleton. 346 * DateFormat provides the timezone, calendar, 347 * full pattern, and date format symbols information. 348 * It should be a SimpleDateFormat object which 349 * has a pattern in it. 350 * the DateIntervalInfo provides the interval patterns. 351 * 352 * @param skeleton the skeleton of the date formatter 353 * @param dtItvInfo the DateIntervalInfo object to be adopted. 354 * @param simpleDateFormat will be used for formatting 355 * 356 * @deprecated This API is ICU internal only. 357 * @hide original deprecated declaration 358 * @hide draft / provisional / internal are hidden on Android 359 */ 360 @Deprecated 361 public DateIntervalFormat(String skeleton, DateIntervalInfo dtItvInfo, 362 SimpleDateFormat simpleDateFormat) 363 { 364 fDateFormat = simpleDateFormat; 365 // freeze date interval info 366 dtItvInfo.freeze(); 367 fSkeleton = skeleton; 368 fInfo = dtItvInfo; 369 isDateIntervalInfoDefault = false; 370 fFromCalendar = (Calendar) fDateFormat.getCalendar().clone(); 371 fToCalendar = (Calendar) fDateFormat.getCalendar().clone(); 372 initializePattern(null); 373 } 374 375 private DateIntervalFormat(String skeleton, ULocale locale, 376 SimpleDateFormat simpleDateFormat) 377 { 378 fDateFormat = simpleDateFormat; 379 fSkeleton = skeleton; 380 fInfo = new DateIntervalInfo(locale).freeze(); 381 isDateIntervalInfoDefault = true; 382 fFromCalendar = (Calendar) fDateFormat.getCalendar().clone(); 383 fToCalendar = (Calendar) fDateFormat.getCalendar().clone(); 384 initializePattern(LOCAL_PATTERN_CACHE); 385} 386 387 388 /** 389 * Construct a DateIntervalFormat from skeleton and the default <code>FORMAT</code> locale. 390 * 391 * This is a convenient override of 392 * getInstance(String skeleton, ULocale locale) 393 * with the value of locale as default <code>FORMAT</code> locale. 394 * 395 * @param skeleton the skeleton on which interval format based. 396 * @return a date time interval formatter. 397 * @see Category#FORMAT 398 */ 399 public static final DateIntervalFormat 400 getInstance(String skeleton) 401 402 { 403 return getInstance(skeleton, ULocale.getDefault(Category.FORMAT)); 404 } 405 406 407 /** 408 * Construct a DateIntervalFormat from skeleton and a given locale. 409 * 410 * This is a convenient override of 411 * getInstance(String skeleton, ULocale locale) 412 * 413 * <p>Example code:{@sample external/icu/android_icu4j/src/samples/java/android/icu/samples/text/dateintervalformat/DateIntervalFormatSample.java dtitvfmtPreDefinedExample} 414 * @param skeleton the skeleton on which interval format based. 415 * @param locale the given locale 416 * @return a date time interval formatter. 417 */ 418 public static final DateIntervalFormat 419 getInstance(String skeleton, Locale locale) 420 { 421 return getInstance(skeleton, ULocale.forLocale(locale)); 422 } 423 424 425 /** 426 * Construct a DateIntervalFormat from skeleton and a given locale. 427 * <P> 428 * In this factory method, 429 * the date interval pattern information is load from resource files. 430 * Users are encouraged to created date interval formatter this way and 431 * to use the pre-defined skeleton macros. 432 * 433 * <P> 434 * There are pre-defined skeletons in DateFormat, 435 * such as MONTH_DAY, YEAR_MONTH_WEEKDAY_DAY etc. 436 * 437 * Those skeletons have pre-defined interval patterns in resource files. 438 * Users are encouraged to use them. 439 * For example: 440 * DateIntervalFormat.getInstance(DateFormat.MONTH_DAY, false, loc); 441 * 442 * The given Locale provides the interval patterns. 443 * For example, for en_GB, if skeleton is YEAR_ABBR_MONTH_WEEKDAY_DAY, 444 * which is "yMMMEEEd", 445 * the interval patterns defined in resource file to above skeleton are: 446 * "EEE, d MMM, yyyy - EEE, d MMM, yyyy" for year differs, 447 * "EEE, d MMM - EEE, d MMM, yyyy" for month differs, 448 * "EEE, d - EEE, d MMM, yyyy" for day differs, 449 * @param skeleton the skeleton on which interval format based. 450 * @param locale the given locale 451 * @return a date time interval formatter. 452 */ 453 public static final DateIntervalFormat 454 getInstance(String skeleton, ULocale locale) 455 { 456 DateTimePatternGenerator generator = DateTimePatternGenerator.getInstance(locale); 457 return new DateIntervalFormat(skeleton, locale, new SimpleDateFormat(generator.getBestPattern(skeleton), locale)); 458 } 459 460 461 462 /** 463 * Construct a DateIntervalFormat from skeleton 464 * DateIntervalInfo, and the default <code>FORMAT</code> locale. 465 * 466 * This is a convenient override of 467 * getInstance(String skeleton, ULocale locale, DateIntervalInfo dtitvinf) 468 * with the locale value as default <code>FORMAT</code> locale. 469 * 470 * @param skeleton the skeleton on which interval format based. 471 * @param dtitvinf the DateIntervalInfo object to be adopted. 472 * @return a date time interval formatter. 473 * @see Category#FORMAT 474 */ 475 public static final DateIntervalFormat getInstance(String skeleton, 476 DateIntervalInfo dtitvinf) 477 { 478 return getInstance(skeleton, ULocale.getDefault(Category.FORMAT), dtitvinf); 479 } 480 481 482 483 /** 484 * Construct a DateIntervalFormat from skeleton 485 * a DateIntervalInfo, and the given locale. 486 * 487 * This is a convenient override of 488 * getInstance(String skeleton, ULocale locale, DateIntervalInfo dtitvinf) 489 * 490 * <p>Example code:{@sample external/icu/android_icu4j/src/samples/java/android/icu/samples/text/dateintervalformat/DateIntervalFormatSample.java dtitvfmtCustomizedExample} 491 * @param skeleton the skeleton on which interval format based. 492 * @param locale the given locale 493 * @param dtitvinf the DateIntervalInfo object to be adopted. 494 * @return a date time interval formatter. 495 */ 496 public static final DateIntervalFormat getInstance(String skeleton, 497 Locale locale, 498 DateIntervalInfo dtitvinf) 499 { 500 return getInstance(skeleton, ULocale.forLocale(locale), dtitvinf); 501 } 502 503 504 505 /** 506 * Construct a DateIntervalFormat from skeleton 507 * a DateIntervalInfo, and the given locale. 508 * 509 * <P> 510 * In this factory method, user provides its own date interval pattern 511 * information, instead of using those pre-defined data in resource file. 512 * This factory method is for powerful users who want to provide their own 513 * interval patterns. 514 * 515 * <P> 516 * There are pre-defined skeleton in DateFormat, 517 * such as MONTH_DAY, YEAR_MONTH_WEEKDAY_DAY etc. 518 * 519 * Those skeletons have pre-defined interval patterns in resource files. 520 * Users are encouraged to use them. 521 * For example: 522 * DateIntervalFormat.getInstance(DateFormat.MONTH_DAY, false, loc,itvinf); 523 * 524 * the DateIntervalInfo provides the interval patterns. 525 * 526 * User are encouraged to set default interval pattern in DateIntervalInfo 527 * as well, if they want to set other interval patterns ( instead of 528 * reading the interval patterns from resource files). 529 * When the corresponding interval pattern for a largest calendar different 530 * field is not found ( if user not set it ), interval format fallback to 531 * the default interval pattern. 532 * If user does not provide default interval pattern, it fallback to 533 * "{date0} - {date1}" 534 * 535 * @param skeleton the skeleton on which interval format based. 536 * @param locale the given locale 537 * @param dtitvinf the DateIntervalInfo object to be adopted. 538 * @return a date time interval formatter. 539 */ 540 public static final DateIntervalFormat getInstance(String skeleton, 541 ULocale locale, 542 DateIntervalInfo dtitvinf) 543 { 544 // clone. If it is frozen, clone returns itself, otherwise, clone 545 // returns a copy. 546 dtitvinf = (DateIntervalInfo)dtitvinf.clone(); 547 DateTimePatternGenerator generator = DateTimePatternGenerator.getInstance(locale); 548 return new DateIntervalFormat(skeleton, dtitvinf, new SimpleDateFormat(generator.getBestPattern(skeleton), locale)); 549 } 550 551 552 /** 553 * Clone this Format object polymorphically. 554 * @return A copy of the object. 555 */ 556 public Object clone() 557 { 558 DateIntervalFormat other = (DateIntervalFormat) super.clone(); 559 other.fDateFormat = (SimpleDateFormat) fDateFormat.clone(); 560 other.fInfo = (DateIntervalInfo) fInfo.clone(); 561 other.fFromCalendar = (Calendar) fFromCalendar.clone(); 562 other.fToCalendar = (Calendar) fToCalendar.clone(); 563 other.fDatePattern = fDatePattern; 564 other.fTimePattern = fTimePattern; 565 other.fDateTimeFormat = fDateTimeFormat; 566 return other; 567 } 568 569 570 /** 571 * Format an object to produce a string. This method handles Formattable 572 * objects with a DateInterval type. 573 * If a the Formattable object type is not a DateInterval, 574 * IllegalArgumentException is thrown. 575 * 576 * @param obj The object to format. 577 * Must be a DateInterval. 578 * @param appendTo Output parameter to receive result. 579 * Result is appended to existing contents. 580 * @param fieldPosition On input: an alignment field, if desired. 581 * On output: the offsets of the alignment field. 582 * There may be multiple instances of a given field type 583 * in an interval format; in this case the fieldPosition 584 * offsets refer to the first instance. 585 * @return Reference to 'appendTo' parameter. 586 * @throws IllegalArgumentException if the formatted object is not 587 * DateInterval object 588 */ 589 public final StringBuffer 590 format(Object obj, StringBuffer appendTo, FieldPosition fieldPosition) 591 { 592 if ( obj instanceof DateInterval ) { 593 return format( (DateInterval)obj, appendTo, fieldPosition); 594 } 595 else { 596 throw new IllegalArgumentException("Cannot format given Object (" + obj.getClass().getName() + ") as a DateInterval"); 597 } 598 } 599 600 /** 601 * Format a DateInterval to produce a string. 602 * 603 * @param dtInterval DateInterval to be formatted. 604 * @param appendTo Output parameter to receive result. 605 * Result is appended to existing contents. 606 * @param fieldPosition On input: an alignment field, if desired. 607 * On output: the offsets of the alignment field. 608 * There may be multiple instances of a given field type 609 * in an interval format; in this case the fieldPosition 610 * offsets refer to the first instance. 611 * @return Reference to 'appendTo' parameter. 612 */ 613 public final StringBuffer format(DateInterval dtInterval, 614 StringBuffer appendTo, 615 FieldPosition fieldPosition) 616 { 617 fFromCalendar.setTimeInMillis(dtInterval.getFromDate()); 618 fToCalendar.setTimeInMillis(dtInterval.getToDate()); 619 return format(fFromCalendar, fToCalendar, appendTo, fieldPosition); 620 } 621 622 /** 623 * @deprecated This API is ICU internal only. 624 * @hide original deprecated declaration 625 * @hide draft / provisional / internal are hidden on Android 626 */ 627 @Deprecated 628 public String getPatterns(Calendar fromCalendar, 629 Calendar toCalendar, 630 Output<String> part2) { 631 // First, find the largest different calendar field. 632 int field; 633 if ( fromCalendar.get(Calendar.ERA) != toCalendar.get(Calendar.ERA) ) { 634 field = Calendar.ERA; 635 } else if ( fromCalendar.get(Calendar.YEAR) != 636 toCalendar.get(Calendar.YEAR) ) { 637 field = Calendar.YEAR; 638 } else if ( fromCalendar.get(Calendar.MONTH) != 639 toCalendar.get(Calendar.MONTH) ) { 640 field = Calendar.MONTH; 641 } else if ( fromCalendar.get(Calendar.DATE) != 642 toCalendar.get(Calendar.DATE) ) { 643 field = Calendar.DATE; 644 } else if ( fromCalendar.get(Calendar.AM_PM) != 645 toCalendar.get(Calendar.AM_PM) ) { 646 field = Calendar.AM_PM; 647 } else if ( fromCalendar.get(Calendar.HOUR) != 648 toCalendar.get(Calendar.HOUR) ) { 649 field = Calendar.HOUR; 650 } else if ( fromCalendar.get(Calendar.MINUTE) != 651 toCalendar.get(Calendar.MINUTE) ) { 652 field = Calendar.MINUTE; 653 } else if ( fromCalendar.get(Calendar.SECOND) != 654 toCalendar.get(Calendar.SECOND) ) { 655 field = Calendar.SECOND; 656 } else { 657 return null; 658 } 659 PatternInfo intervalPattern = fIntervalPatterns.get( 660 DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field]); 661 part2.value = intervalPattern.getSecondPart(); 662 return intervalPattern.getFirstPart(); 663 } 664 /** 665 * Format 2 Calendars to produce a string. 666 * 667 * @param fromCalendar calendar set to the from date in date interval 668 * to be formatted into date interval string 669 * @param toCalendar calendar set to the to date in date interval 670 * to be formatted into date interval string 671 * @param appendTo Output parameter to receive result. 672 * Result is appended to existing contents. 673 * @param pos On input: an alignment field, if desired. 674 * On output: the offsets of the alignment field. 675 * There may be multiple instances of a given field type 676 * in an interval format; in this case the fieldPosition 677 * offsets refer to the first instance. 678 * @return Reference to 'appendTo' parameter. 679 * @throws IllegalArgumentException if the two calendars are not equivalent. 680 */ 681 public final StringBuffer format(Calendar fromCalendar, 682 Calendar toCalendar, 683 StringBuffer appendTo, 684 FieldPosition pos) 685 { 686 // not support different calendar types and time zones 687 if ( !fromCalendar.isEquivalentTo(toCalendar) ) { 688 throw new IllegalArgumentException("can not format on two different calendars"); 689 } 690 691 // First, find the largest different calendar field. 692 int field = -1; //init with an invalid value. 693 694 if ( fromCalendar.get(Calendar.ERA) != toCalendar.get(Calendar.ERA) ) { 695 field = Calendar.ERA; 696 } else if ( fromCalendar.get(Calendar.YEAR) != 697 toCalendar.get(Calendar.YEAR) ) { 698 field = Calendar.YEAR; 699 } else if ( fromCalendar.get(Calendar.MONTH) != 700 toCalendar.get(Calendar.MONTH) ) { 701 field = Calendar.MONTH; 702 } else if ( fromCalendar.get(Calendar.DATE) != 703 toCalendar.get(Calendar.DATE) ) { 704 field = Calendar.DATE; 705 } else if ( fromCalendar.get(Calendar.AM_PM) != 706 toCalendar.get(Calendar.AM_PM) ) { 707 field = Calendar.AM_PM; 708 } else if ( fromCalendar.get(Calendar.HOUR) != 709 toCalendar.get(Calendar.HOUR) ) { 710 field = Calendar.HOUR; 711 } else if ( fromCalendar.get(Calendar.MINUTE) != 712 toCalendar.get(Calendar.MINUTE) ) { 713 field = Calendar.MINUTE; 714 } else if ( fromCalendar.get(Calendar.SECOND) != 715 toCalendar.get(Calendar.SECOND) ) { 716 field = Calendar.SECOND; 717 } else { 718 /* ignore the millisecond etc. small fields' difference. 719 * use single date when all the above are the same. 720 */ 721 return fDateFormat.format(fromCalendar, appendTo, pos); 722 } 723 boolean fromToOnSameDay = (field==Calendar.AM_PM || field==Calendar.HOUR || field==Calendar.MINUTE || field==Calendar.SECOND); 724 725 // get interval pattern 726 PatternInfo intervalPattern = fIntervalPatterns.get( 727 DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field]); 728 729 if ( intervalPattern == null ) { 730 if ( fDateFormat.isFieldUnitIgnored(field) ) { 731 /* the largest different calendar field is small than 732 * the smallest calendar field in pattern, 733 * return single date format. 734 */ 735 return fDateFormat.format(fromCalendar, appendTo, pos); 736 } 737 738 return fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, pos); 739 } 740 741 // If the first part in interval pattern is empty, 742 // the 2nd part of it saves the full-pattern used in fall-back. 743 // For a 'real' interval pattern, the first part will never be empty. 744 if ( intervalPattern.getFirstPart() == null ) { 745 // fall back 746 return fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, pos, 747 intervalPattern.getSecondPart()); 748 } 749 Calendar firstCal; 750 Calendar secondCal; 751 if ( intervalPattern.firstDateInPtnIsLaterDate() ) { 752 firstCal = toCalendar; 753 secondCal = fromCalendar; 754 } else { 755 firstCal = fromCalendar; 756 secondCal = toCalendar; 757 } 758 // break the interval pattern into 2 parts 759 // first part should not be empty, 760 String originalPattern = fDateFormat.toPattern(); 761 fDateFormat.applyPattern(intervalPattern.getFirstPart()); 762 fDateFormat.format(firstCal, appendTo, pos); 763 if ( intervalPattern.getSecondPart() != null ) { 764 fDateFormat.applyPattern(intervalPattern.getSecondPart()); 765 FieldPosition otherPos = new FieldPosition(pos.getField()); 766 fDateFormat.format(secondCal, appendTo, otherPos); 767 if (pos.getEndIndex() == 0 && otherPos.getEndIndex() > 0) { 768 pos = otherPos; 769 } 770 } 771 fDateFormat.applyPattern(originalPattern); 772 return appendTo; 773 } 774 775 private void adjustPosition(String combiningPattern, // has {0} and {1} in it 776 String pat0, FieldPosition pos0, // pattern and pos corresponding to {0} 777 String pat1, FieldPosition pos1, // pattern and pos corresponding to {1} 778 FieldPosition posResult) { 779 int index0 = combiningPattern.indexOf("{0}"); 780 int index1 = combiningPattern.indexOf("{1}"); 781 if (index0 < 0 || index1 < 0) { 782 return; 783 } 784 int placeholderLen = 3; // length of "{0}" or "{1}" 785 if (index0 < index1) { 786 if (pos0.getEndIndex() > 0) { 787 posResult.setBeginIndex(pos0.getBeginIndex() + index0); 788 posResult.setEndIndex(pos0.getEndIndex() + index0); 789 } else if (pos1.getEndIndex() > 0) { 790 // here index1 >= 3 791 index1 += pat0.length() - placeholderLen; // adjust for pat0 replacing {0} 792 posResult.setBeginIndex(pos1.getBeginIndex() + index1); 793 posResult.setEndIndex(pos1.getEndIndex() + index1); 794 } 795 } else { 796 if (pos1.getEndIndex() > 0) { 797 posResult.setBeginIndex(pos1.getBeginIndex() + index1); 798 posResult.setEndIndex(pos1.getEndIndex() + index1); 799 } else if (pos0.getEndIndex() > 0) { 800 // here index0 >= 3 801 index0 += pat1.length() - placeholderLen; // adjust for pat1 replacing {1} 802 posResult.setBeginIndex(pos0.getBeginIndex() + index0); 803 posResult.setEndIndex(pos0.getEndIndex() + index0); 804 } 805 } 806 } 807 808 /* 809 * Format 2 Calendars to using fall-back interval pattern 810 * 811 * The full pattern used in this fall-back format is the 812 * full pattern of the date formatter. 813 * 814 * @param fromCalendar calendar set to the from date in date interval 815 * to be formatted into date interval string 816 * @param toCalendar calendar set to the to date in date interval 817 * to be formatted into date interval string 818 * @param appendTo Output parameter to receive result. 819 * Result is appended to existing contents. 820 * @param pos On input: an alignment field, if desired. 821 * On output: the offsets of the alignment field. 822 * @return Reference to 'appendTo' parameter. 823 */ 824 private final StringBuffer fallbackFormat(Calendar fromCalendar, 825 Calendar toCalendar, 826 boolean fromToOnSameDay, 827 StringBuffer appendTo, 828 FieldPosition pos) { 829 String fullPattern = null; // for saving the pattern in fDateFormat 830 boolean formatDatePlusTimeRange = (fromToOnSameDay && fDatePattern != null && fTimePattern != null); 831 // the fall back 832 if (formatDatePlusTimeRange) { 833 fullPattern = fDateFormat.toPattern(); // save current pattern, restore later 834 fDateFormat.applyPattern(fTimePattern); 835 } 836 FieldPosition otherPos = new FieldPosition(pos.getField()); 837 StringBuffer earlierDate = new StringBuffer(64); 838 earlierDate = fDateFormat.format(fromCalendar, earlierDate, pos); 839 StringBuffer laterDate = new StringBuffer(64); 840 laterDate = fDateFormat.format(toCalendar, laterDate, otherPos); 841 String fallbackPattern = fInfo.getFallbackIntervalPattern(); 842 adjustPosition(fallbackPattern, earlierDate.toString(), pos, laterDate.toString(), otherPos, pos); 843 String fallbackRange = MessageFormat.format(fallbackPattern, new Object[] 844 {earlierDate.toString(), laterDate.toString()}); 845 if (formatDatePlusTimeRange) { 846 // fallbackRange has just the time range, need to format the date part and combine that 847 fDateFormat.applyPattern(fDatePattern); 848 StringBuffer datePortion = new StringBuffer(64); 849 otherPos.setBeginIndex(0); 850 otherPos.setEndIndex(0); 851 datePortion = fDateFormat.format(fromCalendar, datePortion, otherPos); 852 adjustPosition(fDateTimeFormat, fallbackRange, pos, datePortion.toString(), otherPos, pos); 853 fallbackRange = MessageFormat.format(fDateTimeFormat, new Object[] 854 {fallbackRange, datePortion.toString()}); 855 } 856 appendTo.append(fallbackRange); 857 if (formatDatePlusTimeRange) { 858 // restore full pattern 859 fDateFormat.applyPattern(fullPattern); 860 } 861 return appendTo; 862 } 863 864 865 /* 866 * Format 2 Calendars to using fall-back interval pattern 867 * 868 * This fall-back pattern is generated on a given full pattern, 869 * not the full pattern of the date formatter. 870 * 871 * @param fromCalendar calendar set to the from date in date interval 872 * to be formatted into date interval string 873 * @param toCalendar calendar set to the to date in date interval 874 * to be formatted into date interval string 875 * @param appendTo Output parameter to receive result. 876 * Result is appended to existing contents. 877 * @param pos On input: an alignment field, if desired. 878 * On output: the offsets of the alignment field. 879 * @param fullPattern the full pattern need to apply to date formatter 880 * @return Reference to 'appendTo' parameter. 881 */ 882 private final StringBuffer fallbackFormat(Calendar fromCalendar, 883 Calendar toCalendar, 884 boolean fromToOnSameDay, 885 StringBuffer appendTo, 886 FieldPosition pos, 887 String fullPattern) { 888 String originalPattern = fDateFormat.toPattern(); 889 fDateFormat.applyPattern(fullPattern); 890 fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, pos); 891 fDateFormat.applyPattern(originalPattern); 892 return appendTo; 893 } 894 895 896 /** 897 * Date interval parsing is not supported. 898 * <P> 899 * This method should handle parsing of 900 * date time interval strings into Formattable objects with 901 * DateInterval type, which is a pair of UDate. 902 * <P> 903 * Before calling, set parse_pos.index to the offset you want to start 904 * parsing at in the source. After calling, parse_pos.index is the end of 905 * the text you parsed. If error occurs, index is unchanged. 906 * <P> 907 * When parsing, leading whitespace is discarded (with a successful parse), 908 * while trailing whitespace is left as is. 909 * <P> 910 * See Format.parseObject() for more. 911 * 912 * @param source The string to be parsed into an object. 913 * @param parse_pos The position to start parsing at. Since no parsing 914 * is supported, upon return this param is unchanged. 915 * @return A newly created Formattable* object, or NULL 916 * on failure. 917 * @deprecated This API is ICU internal only. 918 * @hide original deprecated declaration 919 * @hide draft / provisional / internal are hidden on Android 920 */ 921 @Deprecated 922 public Object parseObject(String source, ParsePosition parse_pos) 923 { 924 throw new UnsupportedOperationException("parsing is not supported"); 925 } 926 927 928 /** 929 * Gets the date time interval patterns. 930 * @return a copy of the date time interval patterns associated with 931 * this date interval formatter. 932 */ 933 public DateIntervalInfo getDateIntervalInfo() 934 { 935 return (DateIntervalInfo)fInfo.clone(); 936 } 937 938 939 /** 940 * Set the date time interval patterns. 941 * @param newItvPattern the given interval patterns to copy. 942 */ 943 public void setDateIntervalInfo(DateIntervalInfo newItvPattern) 944 { 945 // clone it. If it is frozen, the clone returns itself. 946 // Otherwise, clone returns a copy 947 fInfo = (DateIntervalInfo)newItvPattern.clone(); 948 this.isDateIntervalInfoDefault = false; 949 fInfo.freeze(); // freeze it 950 if ( fDateFormat != null ) { 951 initializePattern(null); 952 } 953 } 954 955 /** 956 * Get the TimeZone 957 * @return A copy of the TimeZone associated with this date interval formatter. 958 */ 959 public TimeZone getTimeZone() 960 { 961 if ( fDateFormat != null ) { 962 // Here we clone, like other getters here, but unlike 963 // DateFormat.getTimeZone() and Calendar.getTimeZone() 964 // which return the TimeZone from the Calendar's zone variable 965 return (TimeZone)(fDateFormat.getTimeZone().clone()); 966 } 967 // If fDateFormat is null (unexpected), return default timezone. 968 return TimeZone.getDefault(); 969 } 970 971 972 /** 973 * Set the TimeZone for the calendar used by this DateIntervalFormat object. 974 * @param zone The new TimeZone, will be cloned for use by this DateIntervalFormat. 975 */ 976 public void setTimeZone(TimeZone zone) 977 { 978 // zone is cloned once for all three usages below: 979 TimeZone zoneToSet = (TimeZone)zone.clone(); 980 if (fDateFormat != null) { 981 fDateFormat.setTimeZone(zoneToSet); 982 } 983 // fDateFormat has the master calendar for the DateIntervalFormat; 984 // fFromCalendar and fToCalendar are internal work clones of that calendar. 985 if (fFromCalendar != null) { 986 fFromCalendar.setTimeZone(zoneToSet); 987 } 988 if (fToCalendar != null) { 989 fToCalendar.setTimeZone(zoneToSet); 990 } 991 } 992 993 /** 994 * Gets the date formatter 995 * @return a copy of the date formatter associated with 996 * this date interval formatter. 997 */ 998 public DateFormat getDateFormat() 999 { 1000 return (DateFormat)fDateFormat.clone(); 1001 } 1002 1003 1004 /* 1005 * Below are for generating interval patterns locale to the formatter 1006 */ 1007 1008 /* 1009 * Initialize interval patterns locale to this formatter. 1010 */ 1011 private void initializePattern(ICUCache<String, Map<String, PatternInfo>> cache) { 1012 String fullPattern = fDateFormat.toPattern(); 1013 ULocale locale = fDateFormat.getLocale(); 1014 String key = null; 1015 Map<String, PatternInfo> patterns = null; 1016 if (cache != null) { 1017 if ( fSkeleton != null ) { 1018 key = locale.toString() + "+" + fullPattern + "+" + fSkeleton; 1019 } else { 1020 key = locale.toString() + "+" + fullPattern; 1021 } 1022 patterns = cache.get(key); 1023 } 1024 if (patterns == null) { 1025 Map<String, PatternInfo> intervalPatterns = initializeIntervalPattern(fullPattern, locale); 1026 patterns = Collections.unmodifiableMap(intervalPatterns); 1027 if (cache != null) { 1028 cache.put(key, patterns); 1029 } 1030 } 1031 fIntervalPatterns = patterns; 1032 } 1033 1034 1035 1036 /* 1037 * Initialize interval patterns locale to this formatter 1038 * 1039 * This code is a bit complicated since 1040 * 1. the interval patterns saved in resource bundle files are interval 1041 * patterns based on date or time only. 1042 * It does not have interval patterns based on both date and time. 1043 * Interval patterns on both date and time are algorithm generated. 1044 * 1045 * For example, it has interval patterns on skeleton "dMy" and "hm", 1046 * but it does not have interval patterns on skeleton "dMyhm". 1047 * 1048 * The rule to generate interval patterns for both date and time skeleton are 1049 * 1) when the year, month, or day differs, concatenate the two original 1050 * expressions with a separator between, 1051 * For example, interval pattern from "Jan 10, 2007 10:10 am" 1052 * to "Jan 11, 2007 10:10am" is 1053 * "Jan 10, 2007 10:10 am - Jan 11, 2007 10:10am" 1054 * 1055 * 2) otherwise, present the date followed by the range expression 1056 * for the time. 1057 * For example, interval pattern from "Jan 10, 2007 10:10 am" 1058 * to "Jan 10, 2007 11:10am" is 1059 * "Jan 10, 2007 10:10 am - 11:10am" 1060 * 1061 * 2. even a pattern does not request a certain calendar field, 1062 * the interval pattern needs to include such field if such fields are 1063 * different between 2 dates. 1064 * For example, a pattern/skeleton is "hm", but the interval pattern 1065 * includes year, month, and date when year, month, and date differs. 1066 * 1067 * 1068 * @param fullPattern formatter's full pattern 1069 * @param locale the given locale. 1070 * @return interval patterns' hash map 1071 */ 1072 private Map<String, PatternInfo> initializeIntervalPattern(String fullPattern, ULocale locale) { 1073 DateTimePatternGenerator dtpng = DateTimePatternGenerator.getInstance(locale); 1074 if ( fSkeleton == null ) { 1075 // fSkeleton is already set by getDateIntervalInstance() 1076 // or by getInstance(String skeleton, .... ) 1077 fSkeleton = dtpng.getSkeleton(fullPattern); 1078 } 1079 String skeleton = fSkeleton; 1080 1081 HashMap<String, PatternInfo> intervalPatterns = new HashMap<String, PatternInfo>(); 1082 1083 /* Check whether the skeleton is a combination of date and time. 1084 * For the complication reason 1 explained above. 1085 */ 1086 StringBuilder date = new StringBuilder(skeleton.length()); 1087 StringBuilder normalizedDate = new StringBuilder(skeleton.length()); 1088 StringBuilder time = new StringBuilder(skeleton.length()); 1089 StringBuilder normalizedTime = new StringBuilder(skeleton.length()); 1090 1091 /* the difference between time skeleton and normalizedTimeSkeleton are: 1092 * 1. (Formerly, normalized time skeleton folded 'H' to 'h'; no longer true) 1093 * 2. 'a' is omitted in normalized time skeleton. 1094 * 3. there is only one appearance for 'h', 'm','v', 'z' in normalized 1095 * time skeleton 1096 * 1097 * The difference between date skeleton and normalizedDateSkeleton are: 1098 * 1. both 'y' and 'd' appear only once in normalizeDateSkeleton 1099 * 2. 'E' and 'EE' are normalized into 'EEE' 1100 * 3. 'MM' is normalized into 'M' 1101 */ 1102 getDateTimeSkeleton(skeleton, date, normalizedDate, 1103 time, normalizedTime); 1104 1105 String dateSkeleton = date.toString(); 1106 String timeSkeleton = time.toString(); 1107 String normalizedDateSkeleton = normalizedDate.toString(); 1108 String normalizedTimeSkeleton = normalizedTime.toString(); 1109 1110 // move this up here since we need it for fallbacks 1111 if (time.length() != 0 && date.length() != 0) { 1112 // Need the Date/Time pattern for concatnation the date with 1113 // the time interval. 1114 // The date/time pattern ( such as {0} {1} ) is saved in 1115 // calendar, that is why need to get the CalendarData here. 1116 CalendarData calData = new CalendarData(locale, null); 1117 String[] patterns = calData.getDateTimePatterns(); 1118 fDateTimeFormat = patterns[8]; 1119 } 1120 1121 boolean found = genSeparateDateTimePtn(normalizedDateSkeleton, 1122 normalizedTimeSkeleton, 1123 intervalPatterns, dtpng); 1124 1125 // for skeletons with seconds, found is false and we enter this block 1126 if ( found == false ) { 1127 // use fallback 1128 // TODO: if user asks "m", but "d" differ 1129 //StringBuffer skeleton = new StringBuffer(skeleton); 1130 if ( time.length() != 0 ) { 1131 //genFallbackForNotFound(Calendar.MINUTE, skeleton); 1132 //genFallbackForNotFound(Calendar.HOUR, skeleton); 1133 //genFallbackForNotFound(Calendar.AM_PM, skeleton); 1134 if ( date.length() == 0 ) { 1135 // prefix with yMd 1136 timeSkeleton = DateFormat.YEAR_NUM_MONTH_DAY + timeSkeleton; 1137 String pattern =dtpng.getBestPattern(timeSkeleton); 1138 // for fall back interval patterns, 1139 // the first part of the pattern is empty, 1140 // the second part of the pattern is the full-pattern 1141 // should be used in fall-back. 1142 PatternInfo ptn = new PatternInfo(null, pattern, 1143 fInfo.getDefaultOrder()); 1144 intervalPatterns.put(DateIntervalInfo. 1145 CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.DATE], ptn); 1146 // share interval pattern 1147 intervalPatterns.put(DateIntervalInfo. 1148 CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.MONTH], ptn); 1149 // share interval pattern 1150 intervalPatterns.put(DateIntervalInfo. 1151 CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.YEAR], ptn); 1152 } else { 1153 //genFallbackForNotFound(Calendar.DATE, skeleton); 1154 //genFallbackForNotFound(Calendar.MONTH, skeleton); 1155 //genFallbackForNotFound(Calendar.YEAR, skeleton); 1156 } 1157 } else { 1158 //genFallbackForNotFound(Calendar.DATE, skeleton); 1159 //genFallbackForNotFound(Calendar.MONTH, skeleton); 1160 //genFallbackForNotFound(Calendar.YEAR, skeleton); 1161 } 1162 return intervalPatterns; 1163 } // end of skeleton not found 1164 // interval patterns for skeleton are found in resource 1165 if ( time.length() == 0 ) { 1166 // done 1167 } else if ( date.length() == 0 ) { 1168 // need to set up patterns for y/M/d differ 1169 /* result from following looks confusing. 1170 * for example: 10 10:10 - 11 10:10, it is not 1171 * clear that the first 10 is the 10th day 1172 time.insert(0, 'd'); 1173 genFallbackPattern(Calendar.DATE, time); 1174 time.insert(0, 'M'); 1175 genFallbackPattern(Calendar.MONTH, time); 1176 time.insert(0, 'y'); 1177 genFallbackPattern(Calendar.YEAR, time); 1178 */ 1179 // prefix with yMd 1180 timeSkeleton = DateFormat.YEAR_NUM_MONTH_DAY + timeSkeleton; 1181 String pattern =dtpng.getBestPattern(timeSkeleton); 1182 // for fall back interval patterns, 1183 // the first part of the pattern is empty, 1184 // the second part of the pattern is the full-pattern 1185 // should be used in fall-back. 1186 PatternInfo ptn = new PatternInfo( 1187 null, pattern, fInfo.getDefaultOrder()); 1188 intervalPatterns.put(DateIntervalInfo. 1189 CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.DATE], ptn); 1190 intervalPatterns.put(DateIntervalInfo. 1191 CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.MONTH], ptn); 1192 intervalPatterns.put(DateIntervalInfo. 1193 CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.YEAR], ptn); 1194 } else { 1195 /* if both present, 1196 * 1) when the year, month, or day differs, 1197 * concatenate the two original expressions with a separator between, 1198 * 2) otherwise, present the date followed by the 1199 * range expression for the time. 1200 */ 1201 /* 1202 * 1) when the year, month, or day differs, 1203 * concatenate the two original expressions with a separator between, 1204 */ 1205 // if field exists, use fall back 1206 if ( !fieldExistsInSkeleton(Calendar.DATE, dateSkeleton) ) { 1207 // prefix skeleton with 'd' 1208 skeleton = DateIntervalInfo. 1209 CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.DATE] + skeleton; 1210 genFallbackPattern(Calendar.DATE, skeleton, intervalPatterns, dtpng); 1211 } 1212 if ( !fieldExistsInSkeleton(Calendar.MONTH, dateSkeleton) ) { 1213 // then prefix skeleton with 'M' 1214 skeleton = DateIntervalInfo. 1215 CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.MONTH] + skeleton; 1216 genFallbackPattern(Calendar.MONTH, skeleton, intervalPatterns, dtpng); 1217 } 1218 if ( !fieldExistsInSkeleton(Calendar.YEAR, dateSkeleton) ) { 1219 // then prefix skeleton with 'y' 1220 skeleton = DateIntervalInfo. 1221 CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.YEAR] + skeleton; 1222 genFallbackPattern(Calendar.YEAR, skeleton, intervalPatterns, dtpng); 1223 } 1224 1225 /* 1226 * 2) otherwise, present the date followed by the 1227 * range expression for the time. 1228 */ 1229 if (fDateTimeFormat == null) { 1230 fDateTimeFormat = "{1} {0}"; 1231 } 1232 String datePattern =dtpng.getBestPattern(dateSkeleton); 1233 concatSingleDate2TimeInterval(fDateTimeFormat, datePattern, Calendar.AM_PM, intervalPatterns); 1234 concatSingleDate2TimeInterval(fDateTimeFormat, datePattern, Calendar.HOUR, intervalPatterns); 1235 concatSingleDate2TimeInterval(fDateTimeFormat, datePattern, Calendar.MINUTE, intervalPatterns); 1236 } 1237 1238 return intervalPatterns; 1239 } 1240 1241 1242 /* 1243 * Generate fall back interval pattern given a calendar field, 1244 * a skeleton, and a date time pattern generator 1245 * @param field the largest different calendar field 1246 * @param skeleton a skeleton 1247 * @param dtpng date time pattern generator 1248 * @param intervalPatterns interval patterns 1249 */ 1250 private void genFallbackPattern(int field, String skeleton, 1251 Map<String, PatternInfo> intervalPatterns, 1252 DateTimePatternGenerator dtpng) { 1253 String pattern = dtpng.getBestPattern(skeleton); 1254 // for fall back interval patterns, 1255 // the first part of the pattern is empty, 1256 // the second part of the pattern is the full-pattern 1257 // should be used in fall-back. 1258 PatternInfo ptn = new PatternInfo( 1259 null, pattern, fInfo.getDefaultOrder()); 1260 intervalPatterns.put( 1261 DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field], ptn); 1262 } 1263 1264 1265 1266 /* 1267 private void genFallbackForNotFound(String field, StringBuffer skeleton) { 1268 if ( SimpleDateFormat.isFieldUnitIgnored(skeleton.toString(), field) ) { 1269 // single date 1270 DateIntervalInfo.PatternInfo ptnInfo = 1271 new DateIntervalInfo.PatternInfo(null, fDateFormat.toPattern(), 1272 fInfo.getDefaultOrder()); 1273 fIntervalPatterns.put(field, ptnInfo); 1274 return; 1275 } else if ( skeleton.indexOf(field) == -1 ) { 1276 skeleton.insert(0,field); 1277 genFallbackPattern(field, skeleton, dtpng); 1278 } 1279 } 1280 */ 1281 1282 /* 1283 * get separated date and time skeleton from a combined skeleton. 1284 * 1285 * The difference between date skeleton and normalizedDateSkeleton are: 1286 * 1. both 'y' and 'd' are appeared only once in normalizeDateSkeleton 1287 * 2. 'E' and 'EE' are normalized into 'EEE' 1288 * 3. 'MM' is normalized into 'M' 1289 * 1290 ** the difference between time skeleton and normalizedTimeSkeleton are: 1291 * 1. both 'H' and 'h' are normalized as 'h' in normalized time skeleton, 1292 * 2. 'a' is omitted in normalized time skeleton. 1293 * 3. there is only one appearance for 'h', 'm','v', 'z' in normalized time 1294 * skeleton 1295 * 1296 * 1297 * @param skeleton given combined skeleton. 1298 * @param date Output parameter for date only skeleton. 1299 * @param normalizedDate Output parameter for normalized date only 1300 * 1301 * @param time Output parameter for time only skeleton. 1302 * @param normalizedTime Output parameter for normalized time only 1303 * skeleton. 1304 */ 1305 private static void getDateTimeSkeleton(String skeleton, 1306 StringBuilder dateSkeleton, 1307 StringBuilder normalizedDateSkeleton, 1308 StringBuilder timeSkeleton, 1309 StringBuilder normalizedTimeSkeleton) 1310 { 1311 // dateSkeleton follows the sequence of y*M*E*d* 1312 // timeSkeleton follows the sequence of hm*[v|z]? 1313 int i; 1314 int ECount = 0; 1315 int dCount = 0; 1316 int MCount = 0; 1317 int yCount = 0; 1318 int hCount = 0; 1319 int HCount = 0; 1320 int mCount = 0; 1321 int vCount = 0; 1322 int zCount = 0; 1323 1324 for (i = 0; i < skeleton.length(); ++i) { 1325 char ch = skeleton.charAt(i); 1326 switch ( ch ) { 1327 case 'E': 1328 dateSkeleton.append(ch); 1329 ++ECount; 1330 break; 1331 case 'd': 1332 dateSkeleton.append(ch); 1333 ++dCount; 1334 break; 1335 case 'M': 1336 dateSkeleton.append(ch); 1337 ++MCount; 1338 break; 1339 case 'y': 1340 dateSkeleton.append(ch); 1341 ++yCount; 1342 break; 1343 case 'G': 1344 case 'Y': 1345 case 'u': 1346 case 'Q': 1347 case 'q': 1348 case 'L': 1349 case 'l': 1350 case 'W': 1351 case 'w': 1352 case 'D': 1353 case 'F': 1354 case 'g': 1355 case 'e': 1356 case 'c': 1357 case 'U': 1358 case 'r': 1359 normalizedDateSkeleton.append(ch); 1360 dateSkeleton.append(ch); 1361 break; 1362 case 'a': 1363 // 'a' is implicitly handled 1364 timeSkeleton.append(ch); 1365 break; 1366 case 'h': 1367 timeSkeleton.append(ch); 1368 ++hCount; 1369 break; 1370 case 'H': 1371 timeSkeleton.append(ch); 1372 ++HCount; 1373 break; 1374 case 'm': 1375 timeSkeleton.append(ch); 1376 ++mCount; 1377 break; 1378 case 'z': 1379 ++zCount; 1380 timeSkeleton.append(ch); 1381 break; 1382 case 'v': 1383 ++vCount; 1384 timeSkeleton.append(ch); 1385 break; 1386 case 'V': 1387 case 'Z': 1388 case 'k': 1389 case 'K': 1390 case 'j': 1391 case 's': 1392 case 'S': 1393 case 'A': 1394 timeSkeleton.append(ch); 1395 normalizedTimeSkeleton.append(ch); 1396 break; 1397 } 1398 } 1399 1400 /* generate normalized form for date*/ 1401 if ( yCount != 0 ) { 1402 for (i = 0; i < yCount; i++) { 1403 normalizedDateSkeleton.append('y'); 1404 } 1405 } 1406 if ( MCount != 0 ) { 1407 if ( MCount < 3 ) { 1408 normalizedDateSkeleton.append('M'); 1409 } else { 1410 for ( i = 0; i < MCount && i < 5; ++i ) { 1411 normalizedDateSkeleton.append('M'); 1412 } 1413 } 1414 } 1415 if ( ECount != 0 ) { 1416 if ( ECount <= 3 ) { 1417 normalizedDateSkeleton.append('E'); 1418 } else { 1419 for ( i = 0; i < ECount && i < 5; ++i ) { 1420 normalizedDateSkeleton.append('E'); 1421 } 1422 } 1423 } 1424 if ( dCount != 0 ) { 1425 normalizedDateSkeleton.append('d'); 1426 } 1427 1428 /* generate normalized form for time */ 1429 if ( HCount != 0 ) { 1430 normalizedTimeSkeleton.append('H'); 1431 } 1432 else if ( hCount != 0 ) { 1433 normalizedTimeSkeleton.append('h'); 1434 } 1435 if ( mCount != 0 ) { 1436 normalizedTimeSkeleton.append('m'); 1437 } 1438 if ( zCount != 0 ) { 1439 normalizedTimeSkeleton.append('z'); 1440 } 1441 if ( vCount != 0 ) { 1442 normalizedTimeSkeleton.append('v'); 1443 } 1444 } 1445 1446 1447 1448 /* 1449 * Generate date or time interval pattern from resource. 1450 * 1451 * It needs to handle the following: 1452 * 1. need to adjust field width. 1453 * For example, the interval patterns saved in DateIntervalInfo 1454 * includes "dMMMy", but not "dMMMMy". 1455 * Need to get interval patterns for dMMMMy from dMMMy. 1456 * Another example, the interval patterns saved in DateIntervalInfo 1457 * includes "hmv", but not "hmz". 1458 * Need to get interval patterns for "hmz' from 'hmv' 1459 * 1460 * 2. there might be no pattern for 'y' differ for skeleton "Md", 1461 * in order to get interval patterns for 'y' differ, 1462 * need to look for it from skeleton 'yMd' 1463 * 1464 * @param dateSkeleton normalized date skeleton 1465 * @param timeSkeleton normalized time skeleton 1466 * @param intervalPatterns interval patterns 1467 * @return whether there is interval patterns for the skeleton. 1468 * true if there is, false otherwise 1469 */ 1470 private boolean genSeparateDateTimePtn(String dateSkeleton, 1471 String timeSkeleton, 1472 Map<String, PatternInfo> intervalPatterns, 1473 DateTimePatternGenerator dtpng) 1474 { 1475 String skeleton; 1476 // if both date and time skeleton present, 1477 // the final interval pattern might include time interval patterns 1478 // ( when, am_pm, hour, minute, second differ ), 1479 // but not date interval patterns ( when year, month, day differ ). 1480 // For year/month/day differ, it falls back to fall-back pattern. 1481 if ( timeSkeleton.length() != 0 ) { 1482 skeleton = timeSkeleton; 1483 } else { 1484 skeleton = dateSkeleton; 1485 } 1486 1487 /* interval patterns for skeleton "dMMMy" (but not "dMMMMy") 1488 * are defined in resource, 1489 * interval patterns for skeleton "dMMMMy" are calculated by 1490 * 1. get the best match skeleton for "dMMMMy", which is "dMMMy" 1491 * 2. get the interval patterns for "dMMMy", 1492 * 3. extend "MMM" to "MMMM" in above interval patterns for "dMMMMy" 1493 * getBestSkeleton() is step 1. 1494 */ 1495 // best skeleton, and the difference information 1496 BestMatchInfo retValue = fInfo.getBestSkeleton(skeleton); 1497 String bestSkeleton = retValue.bestMatchSkeleton; 1498 int differenceInfo = retValue.bestMatchDistanceInfo; 1499 1500 // Set patterns for fallback use, need to do this 1501 // before returning if differenceInfo == -1 1502 if (dateSkeleton.length() != 0 ) { 1503 fDatePattern = dtpng.getBestPattern(dateSkeleton); 1504 } 1505 if (timeSkeleton.length() != 0 ) { 1506 fTimePattern = dtpng.getBestPattern(timeSkeleton); 1507 } 1508 1509 // difference: 1510 // 0 means the best matched skeleton is the same as input skeleton 1511 // 1 means the fields are the same, but field width are different 1512 // 2 means the only difference between fields are v/z, 1513 // -1 means there are other fields difference 1514 // (this will happen, for instance, if the supplied skeleton has seconds, 1515 // but no skeletons in the intervalFormats data do) 1516 if ( differenceInfo == -1 ) { 1517 // skeleton has different fields, not only v/z difference 1518 return false; 1519 } 1520 1521 if ( timeSkeleton.length() == 0 ) { 1522 // only has date skeleton 1523 genIntervalPattern(Calendar.DATE, skeleton, bestSkeleton, differenceInfo, intervalPatterns); 1524 SkeletonAndItsBestMatch skeletons = genIntervalPattern( 1525 Calendar.MONTH, skeleton, 1526 bestSkeleton, differenceInfo, 1527 intervalPatterns); 1528 if ( skeletons != null ) { 1529 bestSkeleton = skeletons.skeleton; 1530 skeleton = skeletons.bestMatchSkeleton; 1531 } 1532 genIntervalPattern(Calendar.YEAR, skeleton, bestSkeleton, differenceInfo, intervalPatterns); 1533 } else { 1534 genIntervalPattern(Calendar.MINUTE, skeleton, bestSkeleton, differenceInfo, intervalPatterns); 1535 genIntervalPattern(Calendar.HOUR, skeleton, bestSkeleton, differenceInfo, intervalPatterns); 1536 genIntervalPattern(Calendar.AM_PM, skeleton, bestSkeleton, differenceInfo, intervalPatterns); 1537 } 1538 return true; 1539 1540 } 1541 1542 1543 1544 /* 1545 * Generate interval pattern from existing resource 1546 * 1547 * It not only save the interval patterns, 1548 * but also return the skeleton and its best match skeleton. 1549 * 1550 * @param field largest different calendar field 1551 * @param skeleton skeleton 1552 * @param bestSkeleton the best match skeleton which has interval pattern 1553 * defined in resource 1554 * @param differenceInfo the difference between skeleton and best skeleton 1555 * 0 means the best matched skeleton is the same as input skeleton 1556 * 1 means the fields are the same, but field width are different 1557 * 2 means the only difference between fields are v/z, 1558 * -1 means there are other fields difference 1559 * 1560 * @param intervalPatterns interval patterns 1561 * 1562 * @return an extended skeleton or extended best skeleton if applicable. 1563 * null otherwise. 1564 */ 1565 private SkeletonAndItsBestMatch genIntervalPattern( 1566 int field, String skeleton, String bestSkeleton, 1567 int differenceInfo, Map<String, PatternInfo> intervalPatterns) { 1568 SkeletonAndItsBestMatch retValue = null; 1569 PatternInfo pattern = fInfo.getIntervalPattern( 1570 bestSkeleton, field); 1571 if ( pattern == null ) { 1572 // single date 1573 if ( SimpleDateFormat.isFieldUnitIgnored(bestSkeleton, field) ) { 1574 PatternInfo ptnInfo = 1575 new PatternInfo(fDateFormat.toPattern(), 1576 null, 1577 fInfo.getDefaultOrder()); 1578 intervalPatterns.put(DateIntervalInfo. 1579 CALENDAR_FIELD_TO_PATTERN_LETTER[field], ptnInfo); 1580 return null; 1581 } 1582 1583 // for 24 hour system, interval patterns in resource file 1584 // might not include pattern when am_pm differ, 1585 // which should be the same as hour differ. 1586 // add it here for simplicity 1587 if ( field == Calendar.AM_PM ) { 1588 pattern = fInfo.getIntervalPattern(bestSkeleton, 1589 Calendar.HOUR); 1590 if ( pattern != null ) { 1591 // share 1592 intervalPatterns.put(DateIntervalInfo. 1593 CALENDAR_FIELD_TO_PATTERN_LETTER[field], 1594 pattern); 1595 } 1596 return null; 1597 } 1598 // else, looking for pattern when 'y' differ for 'dMMMM' skeleton, 1599 // first, get best match pattern "MMMd", 1600 // since there is no pattern for 'y' differs for skeleton 'MMMd', 1601 // need to look for it from skeleton 'yMMMd', 1602 // if found, adjust field width in interval pattern from 1603 // "MMM" to "MMMM". 1604 String fieldLetter = 1605 DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field]; 1606 bestSkeleton = fieldLetter + bestSkeleton; 1607 skeleton = fieldLetter + skeleton; 1608 // for example, looking for patterns when 'y' differ for 1609 // skeleton "MMMM". 1610 pattern = fInfo.getIntervalPattern(bestSkeleton, field); 1611 if ( pattern == null && differenceInfo == 0 ) { 1612 // if there is no skeleton "yMMMM" defined, 1613 // look for the best match skeleton, for example: "yMMM" 1614 BestMatchInfo tmpRetValue = fInfo.getBestSkeleton(skeleton); 1615 String tmpBestSkeleton = tmpRetValue.bestMatchSkeleton; 1616 differenceInfo = tmpRetValue.bestMatchDistanceInfo; 1617 if ( tmpBestSkeleton.length() != 0 && differenceInfo != -1 ) { 1618 pattern = fInfo.getIntervalPattern(tmpBestSkeleton, field); 1619 bestSkeleton = tmpBestSkeleton; 1620 } 1621 } 1622 if ( pattern != null ) { 1623 retValue = new SkeletonAndItsBestMatch(skeleton, bestSkeleton); 1624 } 1625 } 1626 if ( pattern != null ) { 1627 if ( differenceInfo != 0 ) { 1628 String part1 = adjustFieldWidth(skeleton, bestSkeleton, 1629 pattern.getFirstPart(), differenceInfo); 1630 String part2 = adjustFieldWidth(skeleton, bestSkeleton, 1631 pattern.getSecondPart(), differenceInfo); 1632 pattern = new PatternInfo(part1, part2, 1633 pattern.firstDateInPtnIsLaterDate()); 1634 } else { 1635 // pattern is immutable, no need to clone; 1636 // pattern = (PatternInfo)pattern.clone(); 1637 } 1638 intervalPatterns.put( 1639 DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field], pattern); 1640 } 1641 return retValue; 1642 } 1643 1644 /* 1645 * Adjust field width in best match interval pattern to match 1646 * the field width in input skeleton. 1647 * 1648 * TODO (xji) make a general solution 1649 * The adjusting rule can be: 1650 * 1. always adjust 1651 * 2. never adjust 1652 * 3. default adjust, which means adjust according to the following rules 1653 * 3.1 always adjust string, such as MMM and MMMM 1654 * 3.2 never adjust between string and numeric, such as MM and MMM 1655 * 3.3 always adjust year 1656 * 3.4 do not adjust 'd', 'h', or 'm' if h presents 1657 * 3.5 do not adjust 'M' if it is numeric(?) 1658 * 1659 * Since date interval format is well-formed format, 1660 * date and time skeletons are normalized previously, 1661 * till this stage, the adjust here is only "adjust strings, such as MMM 1662 * and MMMM, EEE and EEEE. 1663 * 1664 * @param inputSkeleton the input skeleton 1665 * @param bestMatchSkeleton the best match skeleton 1666 * @param bestMatchIntervalpattern the best match interval pattern 1667 * @param differenceInfo the difference between 2 skeletons 1668 * 1 means only field width differs 1669 * 2 means v/z exchange 1670 * @return the adjusted interval pattern 1671 */ 1672 private static String adjustFieldWidth(String inputSkeleton, 1673 String bestMatchSkeleton, 1674 String bestMatchIntervalPattern, 1675 int differenceInfo ) { 1676 1677 if ( bestMatchIntervalPattern == null ) { 1678 return null; // the 2nd part could be null 1679 } 1680 int[] inputSkeletonFieldWidth = new int[58]; 1681 int[] bestMatchSkeletonFieldWidth = new int[58]; 1682 1683 /* initialize as following 1684 { 1685 // A B C D E F G H I J K L M N O 1686 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1687 // P Q R S T U V W X Y Z 1688 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1689 // a b c d e f g h i j k l m n o 1690 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1691 // p q r s t u v w x y z 1692 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1693 }; 1694 */ 1695 1696 1697 DateIntervalInfo.parseSkeleton(inputSkeleton, inputSkeletonFieldWidth); 1698 DateIntervalInfo.parseSkeleton(bestMatchSkeleton, bestMatchSkeletonFieldWidth); 1699 if ( differenceInfo == 2 ) { 1700 bestMatchIntervalPattern = bestMatchIntervalPattern.replace('v', 'z'); 1701 } 1702 1703 StringBuilder adjustedPtn = new StringBuilder(bestMatchIntervalPattern); 1704 1705 boolean inQuote = false; 1706 char prevCh = 0; 1707 int count = 0; 1708 1709 int PATTERN_CHAR_BASE = 0x41; 1710 1711 // loop through the pattern string character by character 1712 int adjustedPtnLength = adjustedPtn.length(); 1713 for (int i = 0; i < adjustedPtnLength; ++i) { 1714 char ch = adjustedPtn.charAt(i); 1715 if (ch != prevCh && count > 0) { 1716 // check the repeativeness of pattern letter 1717 char skeletonChar = prevCh; 1718 if ( skeletonChar == 'L' ) { 1719 // for skeleton "M+", the pattern is "...L..." 1720 skeletonChar = 'M'; 1721 } 1722 int fieldCount = bestMatchSkeletonFieldWidth[skeletonChar - PATTERN_CHAR_BASE]; 1723 int inputFieldCount = inputSkeletonFieldWidth[skeletonChar - PATTERN_CHAR_BASE]; 1724 if ( fieldCount == count && inputFieldCount > fieldCount ) { 1725 count = inputFieldCount - fieldCount; 1726 for ( int j = 0; j < count; ++j ) { 1727 adjustedPtn.insert(i, prevCh); 1728 } 1729 i += count; 1730 adjustedPtnLength += count; 1731 } 1732 count = 0; 1733 } 1734 if (ch == '\'') { 1735 // Consecutive single quotes are a single quote literal, 1736 // either outside of quotes or between quotes 1737 if ((i+1) < adjustedPtn.length() && adjustedPtn.charAt(i+1) == '\'') { 1738 ++i; 1739 } else { 1740 inQuote = ! inQuote; 1741 } 1742 } 1743 else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) 1744 || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { 1745 // ch is a date-time pattern character 1746 prevCh = ch; 1747 ++count; 1748 } 1749 } 1750 if ( count > 0 ) { 1751 // last item 1752 // check the repeativeness of pattern letter 1753 char skeletonChar = prevCh; 1754 if ( skeletonChar == 'L' ) { 1755 // for skeleton "M+", the pattern is "...L..." 1756 skeletonChar = 'M'; 1757 } 1758 int fieldCount = bestMatchSkeletonFieldWidth[skeletonChar - PATTERN_CHAR_BASE]; 1759 int inputFieldCount = inputSkeletonFieldWidth[skeletonChar - PATTERN_CHAR_BASE]; 1760 if ( fieldCount == count && inputFieldCount > fieldCount ) { 1761 count = inputFieldCount - fieldCount; 1762 for ( int j = 0; j < count; ++j ) { 1763 adjustedPtn.append(prevCh); 1764 } 1765 } 1766 } 1767 return adjustedPtn.toString(); 1768 } 1769 1770 1771 /* 1772 * Concat a single date pattern with a time interval pattern, 1773 * set it into the intervalPatterns, while field is time field. 1774 * This is used to handle time interval patterns on skeleton with 1775 * both time and date. Present the date followed by 1776 * the range expression for the time. 1777 * @param dtfmt date and time format 1778 * @param datePattern date pattern 1779 * @param field time calendar field: AM_PM, HOUR, MINUTE 1780 * @param intervalPatterns interval patterns 1781 */ 1782 private void concatSingleDate2TimeInterval(String dtfmt, 1783 String datePattern, 1784 int field, 1785 Map<String, PatternInfo> intervalPatterns) 1786 { 1787 1788 PatternInfo timeItvPtnInfo = 1789 intervalPatterns.get(DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field]); 1790 if ( timeItvPtnInfo != null ) { 1791 String timeIntervalPattern = timeItvPtnInfo.getFirstPart() + 1792 timeItvPtnInfo.getSecondPart(); 1793 String pattern = MessageFormat.format(dtfmt, new Object[] 1794 {timeIntervalPattern, datePattern}); 1795 timeItvPtnInfo = DateIntervalInfo.genPatternInfo(pattern, 1796 timeItvPtnInfo.firstDateInPtnIsLaterDate()); 1797 intervalPatterns.put( 1798 DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field], timeItvPtnInfo); 1799 } 1800 // else: fall back 1801 // it should not happen if the interval format defined is valid 1802 } 1803 1804 1805 /* 1806 * check whether a calendar field present in a skeleton. 1807 * @param field calendar field need to check 1808 * @param skeleton given skeleton on which to check the calendar field 1809 * @return true if field present in a skeleton. 1810 */ 1811 private static boolean fieldExistsInSkeleton(int field, String skeleton) 1812 { 1813 String fieldChar = DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field]; 1814 return ( (skeleton.indexOf(fieldChar) == -1) ? false : true ) ; 1815 } 1816 1817 1818 /* 1819 * readObject. 1820 */ 1821 private void readObject(ObjectInputStream stream) 1822 throws IOException, ClassNotFoundException { 1823 stream.defaultReadObject(); 1824 initializePattern(isDateIntervalInfoDefault ? LOCAL_PATTERN_CACHE : null); 1825 } 1826 1827 /** 1828 * Get the internal patterns for the skeleton 1829 * @deprecated This API is ICU internal only. 1830 * @hide original deprecated declaration 1831 * @hide draft / provisional / internal are hidden on Android 1832 */ 1833 @Deprecated 1834 public Map<String, PatternInfo> getRawPatterns() { 1835 // this is unmodifiable, so ok to return directly 1836 return fIntervalPatterns; 1837 } 1838} 1839