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