17935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/*
2f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert*   Copyright (C) 2008-2015, International Business Machines
37935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert*   Corporation and others.  All Rights Reserved.
47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert*/
57935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
67935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.text;
77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.IOException;
97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.ObjectInputStream;
107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.FieldPosition;
117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.ParsePosition;
127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Collections;
137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.HashMap;
147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Locale;
157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Map;
167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.CalendarData;
187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.ICUCache;
197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.SimpleCache;
207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.DateIntervalInfo.PatternInfo;
217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.Calendar;
227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.DateInterval;
237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.Output;
247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.TimeZone;
257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ULocale;
267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ULocale.Category;
277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/**
307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * DateIntervalFormat is a class for formatting and parsing date
317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * intervals in a language-independent manner.
327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Only formatting is supported. Parsing is not supported.
337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Date interval means from one date to another date,
367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * for example, from "Jan 11, 2008" to "Jan 18, 2008".
377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * We introduced class DateInterval to represent it.
387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * DateInterval is a pair of UDate, which is
397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the standard milliseconds since 24:00 GMT, Jan 1, 1970.
407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * DateIntervalFormat formats a DateInterval into
437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * text as compactly as possible.
447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For example, the date interval format from "Jan 11, 2008" to "Jan 18,. 2008"
457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * is "Jan 11-18, 2008" for English.
467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * And it parses text into DateInterval,
477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * although initially, parsing is not supported.
487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * There is no structural information in date time patterns.
517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For any punctuations and string literals inside a date time pattern,
527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * we do not know whether it is just a separator, or a prefix, or a suffix.
537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Without such information, so, it is difficult to generate a sub-pattern
547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * (or super-pattern) by algorithm.
557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * So, formatting a DateInterval is pattern-driven. It is very
567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * similar to formatting in SimpleDateFormat.
577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * We introduce class DateIntervalInfo to save date interval
587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * patterns, similar to date time pattern in SimpleDateFormat.
597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Logically, the interval patterns are mappings
627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * from (skeleton, the_largest_different_calendar_field)
637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * to (date_interval_pattern).
647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * A skeleton
677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <ol>
687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>
697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * only keeps the field pattern letter and ignores all other parts
707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * in a pattern, such as space, punctuations, and string literals.
717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>
727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * hides the order of fields.
737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>
747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * might hide a field's pattern letter length.
757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For those non-digit calendar fields, the pattern letter length is
777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * important, such as MMM, MMMM, and MMMMM; EEE and EEEE,
787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and the field's pattern letter length is honored.
797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For the digit calendar fields,  such as M or MM, d or dd, yy or yyyy,
817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the field pattern length is ignored and the best match, which is defined
827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * in date time patterns, will be returned without honor the field pattern
837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * letter length in skeleton.
847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </ol>
857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The calendar fields we support for interval formatting are:
887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * year, month, date, day-of-week, am-pm, hour, hour-of-day, and minute.
897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Those calendar fields can be defined in the following order:
907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * year >  month > date > hour (in day) >  minute
917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The largest different calendar fields between 2 calendars is the
937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * first different calendar field in above order.
947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For example: the largest different calendar fields between "Jan 10, 2007"
967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and "Feb 20, 2008" is year.
977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For other calendar fields, the compact interval formatting is not
1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * supported. And the interval format will be fall back to fall-back
1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * patterns, which is mostly "{date0} - {date1}".
1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * There is a set of pre-defined static skeleton strings in DateFormat,
1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * There are pre-defined interval patterns for those pre-defined skeletons
1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * in locales' resource files.
1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For example, for a skeleton YEAR_ABBR_MONTH_DAY, which is  "yMMMd",
1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * in  en_US, if the largest different calendar field between date1 and date2
1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * is "year", the date interval pattern  is "MMM d, yyyy - MMM d, yyyy",
1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * such as "Jan 10, 2007 - Jan 10, 2008".
1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If the largest different calendar field between date1 and date2 is "month",
1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the date interval pattern is "MMM d - MMM d, yyyy",
1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * such as "Jan 10 - Feb 10, 2007".
1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If the largest different calendar field between date1 and date2 is "day",
1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the date interval pattern is ""MMM d-d, yyyy", such as "Jan 10-20, 2007".
1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For date skeleton, the interval patterns when year, or month, or date is
1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * different are defined in resource files.
1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For time skeleton, the interval patterns when am/pm, or hour, or minute is
1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * different are defined in resource files.
1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If a skeleton is not found in a locale's DateIntervalInfo, which means
1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the interval patterns for the skeleton is not defined in resource file,
1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the interval pattern will falls back to the interval "fallback" pattern
1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * defined in resource file.
1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If the interval "fallback" pattern is not defined, the default fall-back
1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * is "{date0} - {data1}".
1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For the combination of date and time,
1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The rule to genearte interval patterns are:
1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <ol>
1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>
1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    when the year, month, or day differs, falls back to fall-back
1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    interval pattern, which mostly is the concatenate the two original
1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    expressions with a separator between,
1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    For example, interval pattern from "Jan 10, 2007 10:10 am"
1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    to "Jan 11, 2007 10:10am" is
1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    "Jan 10, 2007 10:10 am - Jan 11, 2007 10:10am"
1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>
1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    otherwise, present the date followed by the range expression
1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    for the time.
1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    For example, interval pattern from "Jan 10, 2007 10:10 am"
1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    to "Jan 10, 2007 11:10am" is "Jan 10, 2007 10:10 am - 11:10am"
1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </ol>
1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If two dates are the same, the interval pattern is the single date pattern.
1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For example, interval pattern from "Jan 10, 2007" to "Jan 10, 2007" is
1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * "Jan 10, 2007".
1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Or if the presenting fields between 2 dates have the exact same values,
1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the interval pattern is the  single date pattern.
1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For example, if user only requests year and month,
1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the interval pattern from "Jan 10, 2007" to "Jan 20, 2007" is "Jan 2007".
1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * DateIntervalFormat needs the following information for correct
1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * formatting: time zone, calendar type, pattern, date format symbols,
1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and date interval patterns.
1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * It can be instantiated in several ways:
1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <ol>
1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>
1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    create an instance using default or given locale plus given skeleton.
1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    Users are encouraged to created date interval formatter this way and
1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    to use the pre-defined skeleton macros, such as
1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    YEAR_NUM_MONTH, which consists the calendar fields and
1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    the format style.
1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </li>
1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>
1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    create an instance using default or given locale plus given skeleton
1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    plus a given DateIntervalInfo.
1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    This factory method is for powerful users who want to provide their own
1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    interval patterns.
1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    Locale provides the timezone, calendar, and format symbols information.
1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    Local plus skeleton provides full pattern information.
1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    DateIntervalInfo provides the date interval patterns.
1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </li>
1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </ol>
1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For the calendar field pattern letter, such as G, y, M, d, a, h, H, m, s etc.
1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * DateIntervalFormat uses the same syntax as that of
1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * DateTime format.
1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Code Sample: general usage
1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <pre>
1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   // the date interval object which the DateIntervalFormat formats on
1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   // and parses into
1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   DateInterval dtInterval = new DateInterval(1000*3600*24L, 1000*3600*24*2L);
1957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   DateIntervalFormat dtIntervalFmt = DateIntervalFormat.getInstance(
1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *                   YEAR_MONTH_DAY, Locale("en", "GB", ""));
1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   StringBuffer str = new StringBuffer("");
1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   FieldPosition pos = new FieldPosition(0);
1997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   // formatting
2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   dtIntervalFmt.format(dtInterval, dateIntervalString, pos);
2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </pre>
2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Code Sample: for powerful users who wants to use their own interval pattern
2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <pre>
2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     import com.ibm.icu.text.DateIntervalInfo;
2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     import com.ibm.icu.text.DateIntervalFormat;
2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     ....................
2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     // Get DateIntervalFormat instance using default locale
2137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     DateIntervalFormat dtitvfmt = DateIntervalFormat.getInstance(YEAR_MONTH_DAY);
2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     // Create an empty DateIntervalInfo object, which does not have any interval patterns inside.
2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     dtitvinf = new DateIntervalInfo();
2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     // a series of set interval patterns.
2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     // Only ERA, YEAR, MONTH, DATE,  DAY_OF_MONTH, DAY_OF_WEEK, AM_PM,  HOUR, HOUR_OF_DAY, and MINUTE  are supported.
2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     dtitvinf.setIntervalPattern("yMMMd", Calendar.YEAR, "'y ~ y'");
2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     dtitvinf.setIntervalPattern("yMMMd", Calendar.MONTH, "yyyy 'diff' MMM d - MMM d");
2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     dtitvinf.setIntervalPattern("yMMMd", Calendar.DATE, "yyyy MMM d ~ d");
2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     dtitvinf.setIntervalPattern("yMMMd", Calendar.HOUR_OF_DAY, "yyyy MMM d HH:mm ~ HH:mm");
2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     // Set fallback interval pattern. Fallback pattern is used when interval pattern is not found.
2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     // If the fall-back pattern is not set,  falls back to {date0} - {date1} if interval pattern is not found.
2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     dtitvinf.setFallbackIntervalPattern("{0} - {1}");
2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     // Set above DateIntervalInfo object as the interval patterns of date interval formatter
2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     dtitvfmt.setDateIntervalInfo(dtitvinf);
2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     // Prepare to format
2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     pos = new FieldPosition(0);
2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     str = new StringBuffer("");
2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     // The 2 calendars should be equivalent, otherwise,  IllegalArgumentException will be thrown by format()
2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     Calendar fromCalendar = (Calendar) dtfmt.getCalendar().clone();
2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     Calendar toCalendar = (Calendar) dtfmt.getCalendar().clone();
2397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     fromCalendar.setTimeInMillis(....);
2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     toCalendar.setTimeInMillis(...);
2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     //Formatting given 2 calendars
2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     dtitvfmt.format(fromCalendar, toCalendar, str, pos);
2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </pre>
2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.0
2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic class DateIntervalFormat extends UFormat {
2517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final long serialVersionUID = 1;
2537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Used to save the information for a skeleton's best match skeleton.
2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * It is package accessible since it is used in DateIntervalInfo too.
2577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final class BestMatchInfo {
2597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // the best match skeleton
2607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final String bestMatchSkeleton;
2617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // 0 means the best matched skeleton is the same as input skeleton
2627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // 1 means the fields are the same, but field width are different
2637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // 2 means the only difference between fields are v/z,
2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // -1 means there are other fields difference
2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final int    bestMatchDistanceInfo;
2667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        BestMatchInfo(String bestSkeleton, int difference) {
2677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            bestMatchSkeleton = bestSkeleton;
2687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            bestMatchDistanceInfo = difference;
2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Used to save the information on a skeleton and its best match.
2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final class SkeletonAndItsBestMatch {
2777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final String skeleton;
2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final String bestMatchSkeleton;
2797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        SkeletonAndItsBestMatch(String skeleton, String bestMatch) {
2807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.skeleton = skeleton;
2817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            bestMatchSkeleton = bestMatch;
2827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Cache for the locale interval pattern
2877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static ICUCache<String, Map<String, PatternInfo>> LOCAL_PATTERN_CACHE =
2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        new SimpleCache<String, Map<String, PatternInfo>>();
2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The interval patterns for this locale.
2927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private DateIntervalInfo     fInfo;
2947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
2967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The DateFormat object used to format single pattern
2977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private SimpleDateFormat     fDateFormat;
2997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
3017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The 2 calendars with the from and to date.
3027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * could re-use the calendar in fDateFormat,
3037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * but keeping 2 calendars make it clear and clean.
3047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private Calendar fFromCalendar;
3067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private Calendar fToCalendar;
3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
3097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Following are transient interval information
3107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * relavent (locale) to this formatter.
3117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private String fSkeleton = null;
3137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
3157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Needed for efficient deserialization. If set, it means we can use the
3167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * cache to initialize fIntervalPatterns.
3177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private boolean isDateIntervalInfoDefault;
3197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  Interval patterns for this instance's locale.
3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private transient Map<String, PatternInfo> fIntervalPatterns = null;
3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * default constructor; private because we don't want anyone to use
3287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @SuppressWarnings("unused")
3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private DateIntervalFormat() {
3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct a DateIntervalFormat from DateFormat,
3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * a DateIntervalInfo, and skeleton.
3367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * DateFormat provides the timezone, calendar,
3377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * full pattern, and date format symbols information.
3387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * It should be a SimpleDateFormat object which
3397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * has a pattern in it.
3407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the DateIntervalInfo provides the interval patterns.
3417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
3427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton  the skeleton of the date formatter
3437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param dtItvInfo  the DateIntervalInfo object to be adopted.
3447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param simpleDateFormat will be used for formatting
3457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
3467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
3477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
3487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
3507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public DateIntervalFormat(String skeleton, DateIntervalInfo dtItvInfo,
3517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                               SimpleDateFormat simpleDateFormat)
3527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
3537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fDateFormat = simpleDateFormat;
3547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // freeze date interval info
3557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        dtItvInfo.freeze();
3567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fSkeleton = skeleton;
3577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fInfo = dtItvInfo;
3587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        isDateIntervalInfoDefault = false;
3597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fFromCalendar = (Calendar) fDateFormat.getCalendar().clone();
3607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fToCalendar = (Calendar) fDateFormat.getCalendar().clone();
3617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        initializePattern(null);
3627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private DateIntervalFormat(String skeleton, ULocale locale,
3657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            SimpleDateFormat simpleDateFormat)
3667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
3677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fDateFormat = simpleDateFormat;
3687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fSkeleton = skeleton;
3697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fInfo = new DateIntervalInfo(locale).freeze();
3707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        isDateIntervalInfoDefault = true;
3717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fFromCalendar = (Calendar) fDateFormat.getCalendar().clone();
3727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fToCalendar = (Calendar) fDateFormat.getCalendar().clone();
3737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        initializePattern(LOCAL_PATTERN_CACHE);
3747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
3757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct a DateIntervalFormat from skeleton and  the default <code>FORMAT</code> locale.
3797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
3807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a convenient override of
3817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * getInstance(String skeleton, ULocale locale)
3827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * with the value of locale as default <code>FORMAT</code> locale.
3837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
3847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton  the skeleton on which interval format based.
3857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return          a date time interval formatter.
3867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see Category#FORMAT
3877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
3887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final DateIntervalFormat
3907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        getInstance(String skeleton)
3917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
3937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getInstance(skeleton, ULocale.getDefault(Category.FORMAT));
3947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct a DateIntervalFormat from skeleton and a given locale.
3997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a convenient override of
4017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * getInstance(String skeleton, ULocale locale)
4027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>Example code:{@.jcite com.ibm.icu.samples.text.dateintervalformat.DateIntervalFormatSample:---dtitvfmtPreDefinedExample}
4047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton  the skeleton on which interval format based.
4057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param locale    the given locale
4067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return          a date time interval formatter.
4077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
4087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final DateIntervalFormat
4107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        getInstance(String skeleton, Locale locale)
4117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
4127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getInstance(skeleton, ULocale.forLocale(locale));
4137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct a DateIntervalFormat from skeleton and a given locale.
4187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <P>
4197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * In this factory method,
4207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the date interval pattern information is load from resource files.
4217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Users are encouraged to created date interval formatter this way and
4227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * to use the pre-defined skeleton macros.
4237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <P>
4257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * There are pre-defined skeletons in DateFormat,
4267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * such as MONTH_DAY, YEAR_MONTH_WEEKDAY_DAY etc.
4277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Those skeletons have pre-defined interval patterns in resource files.
4297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Users are encouraged to use them.
4307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * For example:
4317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * DateIntervalFormat.getInstance(DateFormat.MONTH_DAY, false, loc);
4327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The given Locale provides the interval patterns.
4347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * For example, for en_GB, if skeleton is YEAR_ABBR_MONTH_WEEKDAY_DAY,
4357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * which is "yMMMEEEd",
4367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the interval patterns defined in resource file to above skeleton are:
4377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "EEE, d MMM, yyyy - EEE, d MMM, yyyy" for year differs,
4387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "EEE, d MMM - EEE, d MMM, yyyy" for month differs,
4397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "EEE, d - EEE, d MMM, yyyy" for day differs,
4407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton  the skeleton on which interval format based.
4417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param locale    the given locale
4427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return          a date time interval formatter.
4437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
4447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final DateIntervalFormat
4467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        getInstance(String skeleton, ULocale locale)
4477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
4487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DateTimePatternGenerator generator = DateTimePatternGenerator.getInstance(locale);
4497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new DateIntervalFormat(skeleton, locale, new SimpleDateFormat(generator.getBestPattern(skeleton), locale));
4507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct a DateIntervalFormat from skeleton
4567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  DateIntervalInfo, and the default <code>FORMAT</code> locale.
4577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a convenient override of
4597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * getInstance(String skeleton, ULocale locale, DateIntervalInfo dtitvinf)
4607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * with the locale value as default <code>FORMAT</code> locale.
4617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton  the skeleton on which interval format based.
4637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param dtitvinf  the DateIntervalInfo object to be adopted.
4647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return          a date time interval formatter.
4657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see Category#FORMAT
4667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
4677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final DateIntervalFormat getInstance(String skeleton,
4697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                   DateIntervalInfo dtitvinf)
4707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
4717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getInstance(skeleton, ULocale.getDefault(Category.FORMAT), dtitvinf);
4727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct a DateIntervalFormat from skeleton
4787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * a DateIntervalInfo, and the given locale.
4797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a convenient override of
4817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * getInstance(String skeleton, ULocale locale, DateIntervalInfo dtitvinf)
4827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>Example code:{@.jcite com.ibm.icu.samples.text.dateintervalformat.DateIntervalFormatSample:---dtitvfmtCustomizedExample}
4847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton  the skeleton on which interval format based.
4857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param locale    the given locale
4867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param dtitvinf  the DateIntervalInfo object to be adopted.
4877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return          a date time interval formatter.
4887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
4897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final DateIntervalFormat getInstance(String skeleton,
4917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                 Locale locale,
4927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                 DateIntervalInfo dtitvinf)
4937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
4947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getInstance(skeleton, ULocale.forLocale(locale), dtitvinf);
4957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct a DateIntervalFormat from skeleton
5017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * a DateIntervalInfo, and the given locale.
5027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <P>
5047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * In this factory method, user provides its own date interval pattern
5057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * information, instead of using those pre-defined data in resource file.
5067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This factory method is for powerful users who want to provide their own
5077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * interval patterns.
5087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <P>
5107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * There are pre-defined skeleton in DateFormat,
5117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * such as MONTH_DAY, YEAR_MONTH_WEEKDAY_DAY etc.
5127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Those skeletons have pre-defined interval patterns in resource files.
5147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Users are encouraged to use them.
5157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * For example:
5167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * DateIntervalFormat.getInstance(DateFormat.MONTH_DAY, false, loc,itvinf);
5177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the DateIntervalInfo provides the interval patterns.
5197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * User are encouraged to set default interval pattern in DateIntervalInfo
5217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * as well, if they want to set other interval patterns ( instead of
5227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * reading the interval patterns from resource files).
5237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * When the corresponding interval pattern for a largest calendar different
5247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * field is not found ( if user not set it ), interval format fallback to
5257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the default interval pattern.
5267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If user does not provide default interval pattern, it fallback to
5277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "{date0} - {date1}"
5287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton  the skeleton on which interval format based.
5307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param locale    the given locale
5317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param dtitvinf  the DateIntervalInfo object to be adopted.
5327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return          a date time interval formatter.
5337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
5347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final DateIntervalFormat getInstance(String skeleton,
5367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                 ULocale locale,
5377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                 DateIntervalInfo dtitvinf)
5387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
5397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // clone. If it is frozen, clone returns itself, otherwise, clone
5407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // returns a copy.
5417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        dtitvinf = (DateIntervalInfo)dtitvinf.clone();
5427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DateTimePatternGenerator generator = DateTimePatternGenerator.getInstance(locale);
5437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new DateIntervalFormat(skeleton, dtitvinf, new SimpleDateFormat(generator.getBestPattern(skeleton), locale));
5447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Clone this Format object polymorphically.
5497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return    A copy of the object.
5507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
5517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Object clone()
5537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
5547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DateIntervalFormat other = (DateIntervalFormat) super.clone();
5557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        other.fDateFormat = (SimpleDateFormat) fDateFormat.clone();
5567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        other.fInfo = (DateIntervalInfo) fInfo.clone();
5577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        other.fFromCalendar = (Calendar) fFromCalendar.clone();
5587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        other.fToCalendar = (Calendar) fToCalendar.clone();
5597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return other;
5607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Format an object to produce a string. This method handles Formattable
5657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * objects with a DateInterval type.
5667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If a the Formattable object type is not a DateInterval,
5677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * IllegalArgumentException is thrown.
5687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param obj               The object to format.
5707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          Must be a DateInterval.
5717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param appendTo          Output parameter to receive result.
5727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          Result is appended to existing contents.
5737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param fieldPosition     On input: an alignment field, if desired.
5747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          On output: the offsets of the alignment field.
5757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return                  Reference to 'appendTo' parameter.
5767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws    IllegalArgumentException  if the formatted object is not
5777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                                      DateInterval object
5787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
5797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final StringBuffer
5817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        format(Object obj, StringBuffer appendTo, FieldPosition fieldPosition)
5827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
5837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( obj instanceof DateInterval ) {
5847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return format( (DateInterval)obj, appendTo, fieldPosition);
5857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        else {
5877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException("Cannot format given Object (" + obj.getClass().getName() + ") as a DateInterval");
5887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Format a DateInterval to produce a string.
5937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param dtInterval        DateInterval to be formatted.
5957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param appendTo          Output parameter to receive result.
5967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          Result is appended to existing contents.
5977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param fieldPosition     On input: an alignment field, if desired.
5987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          On output: the offsets of the alignment field.
5997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return                  Reference to 'appendTo' parameter.
6007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
6017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
6027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final StringBuffer format(DateInterval dtInterval,
6037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                     StringBuffer appendTo,
6047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                     FieldPosition fieldPosition)
6057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
6067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fFromCalendar.setTimeInMillis(dtInterval.getFromDate());
6077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fToCalendar.setTimeInMillis(dtInterval.getToDate());
6087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return format(fFromCalendar, fToCalendar, appendTo, fieldPosition);
6097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
6127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
6137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
6147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
6157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
6167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getPatterns(Calendar fromCalendar,
6177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Calendar toCalendar,
6187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Output<String> part2) {
6197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // First, find the largest different calendar field.
6207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int field;
6217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( fromCalendar.get(Calendar.ERA) != toCalendar.get(Calendar.ERA) ) {
6227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.ERA;
6237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.YEAR) !=
6247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.YEAR) ) {
6257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.YEAR;
6267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.MONTH) !=
6277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.MONTH) ) {
6287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.MONTH;
6297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.DATE) !=
6307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.DATE) ) {
6317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.DATE;
6327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.AM_PM) !=
6337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.AM_PM) ) {
6347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.AM_PM;
6357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.HOUR) !=
6367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.HOUR) ) {
6377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.HOUR;
6387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.MINUTE) !=
6397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.MINUTE) ) {
6407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.MINUTE;
6417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
6427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null;
6437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PatternInfo intervalPattern = fIntervalPatterns.get(
6457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field]);
6467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        part2.value = intervalPattern.getSecondPart();
6477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return intervalPattern.getFirstPart();
6487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
6507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Format 2 Calendars to produce a string.
6517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
6527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param fromCalendar      calendar set to the from date in date interval
6537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          to be formatted into date interval string
6547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param toCalendar        calendar set to the to date in date interval
6557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          to be formatted into date interval string
6567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param appendTo          Output parameter to receive result.
6577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          Result is appended to existing contents.
6587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param pos               On input: an alignment field, if desired.
6597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          On output: the offsets of the alignment field.
6607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return                  Reference to 'appendTo' parameter.
6617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws    IllegalArgumentException  if the two calendars are not equivalent.
6627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
6637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
6647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final StringBuffer format(Calendar fromCalendar,
6657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                     Calendar toCalendar,
6667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                     StringBuffer appendTo,
6677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                     FieldPosition pos)
6687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
6697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // not support different calendar types and time zones
6707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( !fromCalendar.isEquivalentTo(toCalendar) ) {
6717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException("can not format on two different calendars");
6727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // First, find the largest different calendar field.
6757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int field = -1; //init with an invalid value.
6767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( fromCalendar.get(Calendar.ERA) != toCalendar.get(Calendar.ERA) ) {
6787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.ERA;
6797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.YEAR) !=
6807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.YEAR) ) {
6817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.YEAR;
6827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.MONTH) !=
6837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.MONTH) ) {
6847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.MONTH;
6857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.DATE) !=
6867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.DATE) ) {
6877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.DATE;
6887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.AM_PM) !=
6897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.AM_PM) ) {
6907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.AM_PM;
6917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.HOUR) !=
6927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.HOUR) ) {
6937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.HOUR;
6947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.MINUTE) !=
6957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.MINUTE) ) {
6967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.MINUTE;
6977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
6987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            /* ignore the second/millisecond etc. small fields' difference.
6997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * use single date when all the above are the same.
7007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             */
7017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return fDateFormat.format(fromCalendar, appendTo, pos);
7027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // get interval pattern
7057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PatternInfo intervalPattern = fIntervalPatterns.get(
7067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field]);
7077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( intervalPattern == null ) {
7097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( fDateFormat.isFieldUnitIgnored(field) ) {
7107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                /* the largest different calendar field is small than
7117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 * the smallest calendar field in pattern,
7127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 * return single date format.
7137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 */
7147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return fDateFormat.format(fromCalendar, appendTo, pos);
7157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return fallbackFormat(fromCalendar, toCalendar, appendTo, pos);
7187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // If the first part in interval pattern is empty,
7217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // the 2nd part of it saves the full-pattern used in fall-back.
7227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // For a 'real' interval pattern, the first part will never be empty.
7237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( intervalPattern.getFirstPart() == null ) {
7247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // fall back
7257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return fallbackFormat(fromCalendar, toCalendar, appendTo, pos,
7267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    intervalPattern.getSecondPart());
7277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Calendar firstCal;
7297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Calendar secondCal;
7307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( intervalPattern.firstDateInPtnIsLaterDate() ) {
7317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            firstCal = toCalendar;
7327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            secondCal = fromCalendar;
7337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
7347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            firstCal = fromCalendar;
7357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            secondCal = toCalendar;
7367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // break the interval pattern into 2 parts
7387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // first part should not be empty,
7397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String originalPattern = fDateFormat.toPattern();
7407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fDateFormat.applyPattern(intervalPattern.getFirstPart());
7417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fDateFormat.format(firstCal, appendTo, pos);
7427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( intervalPattern.getSecondPart() != null ) {
7437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fDateFormat.applyPattern(intervalPattern.getSecondPart());
7447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fDateFormat.format(secondCal, appendTo, pos);
7457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fDateFormat.applyPattern(originalPattern);
7477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return appendTo;
7487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
7527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Format 2 Calendars to using fall-back interval pattern
7537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
7547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The full pattern used in this fall-back format is the
7557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * full pattern of the date formatter.
7567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
7577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param fromCalendar      calendar set to the from date in date interval
7587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          to be formatted into date interval string
7597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param toCalendar        calendar set to the to date in date interval
7607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          to be formatted into date interval string
7617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param appendTo          Output parameter to receive result.
7627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          Result is appended to existing contents.
7637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param pos               On input: an alignment field, if desired.
7647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          On output: the offsets of the alignment field.
7657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return                  Reference to 'appendTo' parameter.
7667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
7677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private final StringBuffer fallbackFormat(Calendar fromCalendar,
7687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                              Calendar toCalendar,
7697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                              StringBuffer appendTo,
7707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                              FieldPosition pos)  {
7717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // the fall back
7727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            StringBuffer earlierDate = new StringBuffer(64);
7737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            earlierDate = fDateFormat.format(fromCalendar, earlierDate, pos);
7747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            StringBuffer laterDate = new StringBuffer(64);
7757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            laterDate = fDateFormat.format(toCalendar, laterDate, pos);
7767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String fallbackPattern = fInfo.getFallbackIntervalPattern();
7777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String fallback = MessageFormat.format(fallbackPattern, new Object[]
7787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            {earlierDate.toString(), laterDate.toString()});
7797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            appendTo.append(fallback);
7807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return appendTo;
7817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
7857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Format 2 Calendars to using fall-back interval pattern
7867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
7877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This fall-back pattern is generated on a given full pattern,
7887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * not the full pattern of the date formatter.
7897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
7907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param fromCalendar      calendar set to the from date in date interval
7917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          to be formatted into date interval string
7927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param toCalendar        calendar set to the to date in date interval
7937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          to be formatted into date interval string
7947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param appendTo          Output parameter to receive result.
7957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          Result is appended to existing contents.
7967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param pos               On input: an alignment field, if desired.
7977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          On output: the offsets of the alignment field.
7987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param fullPattern       the full pattern need to apply to date formatter
7997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return                  Reference to 'appendTo' parameter.
8007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private final StringBuffer fallbackFormat(Calendar fromCalendar,
8027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                              Calendar toCalendar,
8037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                              StringBuffer appendTo,
8047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                              FieldPosition pos,
8057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                              String fullPattern)  {
8067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String originalPattern = fDateFormat.toPattern();
8077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fDateFormat.applyPattern(fullPattern);
8087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fallbackFormat(fromCalendar, toCalendar, appendTo, pos);
8097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fDateFormat.applyPattern(originalPattern);
8107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return appendTo;
8117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Date interval parsing is not supported.
8167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <P>
8177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This method should handle parsing of
8187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * date time interval strings into Formattable objects with
8197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * DateInterval type, which is a pair of UDate.
8207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <P>
8217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <P>
8227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Before calling, set parse_pos.index to the offset you want to start
8237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * parsing at in the source. After calling, parse_pos.index is the end of
8247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the text you parsed. If error occurs, index is unchanged.
8257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <P>
8267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * When parsing, leading whitespace is discarded (with a successful parse),
8277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * while trailing whitespace is left as is.
8287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <P>
8297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * See Format.parseObject() for more.
8307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
8317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param source    The string to be parsed into an object.
8327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param parse_pos The position to start parsing at. Since no parsing
8337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                  is supported, upon return this param is unchanged.
8347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return          A newly created Formattable* object, or NULL
8357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                  on failure.
8367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
8377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
8387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
8407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Object parseObject(String source, ParsePosition parse_pos)
8417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
8427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        throw new UnsupportedOperationException("parsing is not supported");
8437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Gets the date time interval patterns.
8487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return a copy of the date time interval patterns associated with
8497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * this date interval formatter.
8507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
8517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public DateIntervalInfo getDateIntervalInfo()
8537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
8547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return (DateIntervalInfo)fInfo.clone();
8557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Set the date time interval patterns.
8607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param newItvPattern   the given interval patterns to copy.
8617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
8627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void setDateIntervalInfo(DateIntervalInfo newItvPattern)
8647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
8657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // clone it. If it is frozen, the clone returns itself.
8667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Otherwise, clone returns a copy
8677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fInfo = (DateIntervalInfo)newItvPattern.clone();
8687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        this.isDateIntervalInfoDefault = false;
8697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fInfo.freeze(); // freeze it
8707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( fDateFormat != null ) {
8717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            initializePattern(null);
8727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Get the TimeZone
8777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return A copy of the TimeZone associated with this date interval formatter.
878f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @stable ICU 53
8797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public TimeZone getTimeZone()
8817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
8827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( fDateFormat != null ) {
8837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Here we clone, like other getters here, but unlike
8847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // DateFormat.getTimeZone() and Calendar.getTimeZone()
8857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // which return the TimeZone from the Calendar's zone variable
8867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return (TimeZone)(fDateFormat.getTimeZone().clone());
8877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // If fDateFormat is null (unexpected), return default timezone.
8897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return TimeZone.getDefault();
8907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Set the TimeZone for the calendar used by this DateIntervalFormat object.
8957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param zone The new TimeZone, will be cloned for use by this DateIntervalFormat.
896f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @stable ICU 53
8977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void setTimeZone(TimeZone zone)
8997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
9007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // zone is cloned once for all three usages below:
9017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        TimeZone zoneToSet = (TimeZone)zone.clone();
9027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (fDateFormat != null) {
9037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fDateFormat.setTimeZone(zoneToSet);
9047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // fDateFormat has the master calendar for the DateIntervalFormat;
9067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // fFromCalendar and fToCalendar are internal work clones of that calendar.
9077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (fFromCalendar != null) {
9087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fFromCalendar.setTimeZone(zoneToSet);
9097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (fToCalendar != null) {
9117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fToCalendar.setTimeZone(zoneToSet);
9127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Gets the date formatter
9177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return a copy of the date formatter associated with
9187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * this date interval formatter.
9197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
9207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public DateFormat getDateFormat()
9227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
9237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return (DateFormat)fDateFormat.clone();
9247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
9287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  Below are for generating interval patterns locale to the formatter
9297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
9327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Initialize interval patterns locale to this formatter.
9337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void initializePattern(ICUCache<String, Map<String, PatternInfo>> cache) {
9357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String fullPattern = fDateFormat.toPattern();
9367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ULocale locale = fDateFormat.getLocale();
9377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String key = null;
9387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Map<String, PatternInfo> patterns = null;
9397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (cache != null) {
9407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( fSkeleton != null ) {
9417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                key = locale.toString() + "+" + fullPattern + "+" + fSkeleton;
9427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
9437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                key = locale.toString() + "+" + fullPattern;
9447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
9457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            patterns = cache.get(key);
9467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (patterns == null) {
9487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Map<String, PatternInfo> intervalPatterns = initializeIntervalPattern(fullPattern, locale);
9497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            patterns = Collections.unmodifiableMap(intervalPatterns);
9507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (cache != null) {
9517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                cache.put(key, patterns);
9527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
9537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fIntervalPatterns = patterns;
9557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
9607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Initialize interval patterns locale to this formatter
9617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
9627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This code is a bit complicated since
9637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 1. the interval patterns saved in resource bundle files are interval
9647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    patterns based on date or time only.
9657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    It does not have interval patterns based on both date and time.
9667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    Interval patterns on both date and time are algorithm generated.
9677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
9687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    For example, it has interval patterns on skeleton "dMy" and "hm",
9697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    but it does not have interval patterns on skeleton "dMyhm".
9707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
9717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    The rule to generate interval patterns for both date and time skeleton are
9727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    1) when the year, month, or day differs, concatenate the two original
9737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    expressions with a separator between,
9747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    For example, interval pattern from "Jan 10, 2007 10:10 am"
9757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    to "Jan 11, 2007 10:10am" is
9767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    "Jan 10, 2007 10:10 am - Jan 11, 2007 10:10am"
9777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
9787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    2) otherwise, present the date followed by the range expression
9797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    for the time.
9807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    For example, interval pattern from "Jan 10, 2007 10:10 am"
9817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    to "Jan 10, 2007 11:10am" is
9827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    "Jan 10, 2007 10:10 am - 11:10am"
9837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
9847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 2. even a pattern does not request a certain calendar field,
9857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    the interval pattern needs to include such field if such fields are
9867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    different between 2 dates.
9877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    For example, a pattern/skeleton is "hm", but the interval pattern
9887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    includes year, month, and date when year, month, and date differs.
9897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
9907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
9917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param fullPattern  formatter's full pattern
9927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param locale       the given locale.
9937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return             interval patterns' hash map
9947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private Map<String, PatternInfo> initializeIntervalPattern(String fullPattern, ULocale locale) {
9967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DateTimePatternGenerator dtpng = DateTimePatternGenerator.getInstance(locale);
9977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( fSkeleton == null ) {
9987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // fSkeleton is already set by getDateIntervalInstance()
9997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // or by getInstance(String skeleton, .... )
10007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fSkeleton = dtpng.getSkeleton(fullPattern);
10017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String skeleton = fSkeleton;
10037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        HashMap<String, PatternInfo> intervalPatterns = new HashMap<String, PatternInfo>();
10057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /* Check whether the skeleton is a combination of date and time.
10077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * For the complication reason 1 explained above.
10087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
10097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder date = new StringBuilder(skeleton.length());
10107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder normalizedDate = new StringBuilder(skeleton.length());
10117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder time = new StringBuilder(skeleton.length());
10127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder normalizedTime = new StringBuilder(skeleton.length());
10137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /* the difference between time skeleton and normalizedTimeSkeleton are:
10157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * 1. (Formerly, normalized time skeleton folded 'H' to 'h'; no longer true)
10167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * 2. 'a' is omitted in normalized time skeleton.
10177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * 3. there is only one appearance for 'h', 'm','v', 'z' in normalized
10187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *    time skeleton
10197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
10207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * The difference between date skeleton and normalizedDateSkeleton are:
10217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * 1. both 'y' and 'd' appear only once in normalizeDateSkeleton
10227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * 2. 'E' and 'EE' are normalized into 'EEE'
10237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * 3. 'MM' is normalized into 'M'
10247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
10257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        getDateTimeSkeleton(skeleton, date, normalizedDate,
10267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            time, normalizedTime);
10277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String dateSkeleton = date.toString();
10297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String timeSkeleton = time.toString();
10307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String normalizedDateSkeleton = normalizedDate.toString();
10317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String normalizedTimeSkeleton = normalizedTime.toString();
10327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean found = genSeparateDateTimePtn(normalizedDateSkeleton,
10347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                               normalizedTimeSkeleton,
10357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                               intervalPatterns);
10367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( found == false ) {
10387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // use fallback
10397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // TODO: if user asks "m", but "d" differ
10407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //StringBuffer skeleton = new StringBuffer(skeleton);
10417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( time.length() != 0 ) {
10427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //genFallbackForNotFound(Calendar.MINUTE, skeleton);
10437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //genFallbackForNotFound(Calendar.HOUR, skeleton);
10447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //genFallbackForNotFound(Calendar.AM_PM, skeleton);
10457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ( date.length() == 0 ) {
10467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // prefix with yMd
10477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    timeSkeleton = DateFormat.YEAR_NUM_MONTH_DAY + timeSkeleton;
10487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    String pattern =dtpng.getBestPattern(timeSkeleton);
10497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // for fall back interval patterns,
10507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // the first part of the pattern is empty,
10517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // the second part of the pattern is the full-pattern
10527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // should be used in fall-back.
10537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    PatternInfo ptn = new PatternInfo(null, pattern,
10547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                     fInfo.getDefaultOrder());
10557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    intervalPatterns.put(DateIntervalInfo.
10567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.DATE], ptn);
10577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // share interval pattern
10587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    intervalPatterns.put(DateIntervalInfo.
10597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.MONTH], ptn);
10607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // share interval pattern
10617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    intervalPatterns.put(DateIntervalInfo.
10627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.YEAR], ptn);
10637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
10647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //genFallbackForNotFound(Calendar.DATE, skeleton);
10657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //genFallbackForNotFound(Calendar.MONTH, skeleton);
10667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //genFallbackForNotFound(Calendar.YEAR, skeleton);
10677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
10687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
10697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //genFallbackForNotFound(Calendar.DATE, skeleton);
10707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //genFallbackForNotFound(Calendar.MONTH, skeleton);
10717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //genFallbackForNotFound(Calendar.YEAR, skeleton);
10727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return intervalPatterns;
10747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } // end of skeleton not found
10757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // interval patterns for skeleton are found in resource
10767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( time.length() == 0 ) {
10777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // done
10787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( date.length() == 0 ) {
10797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // need to set up patterns for y/M/d differ
10807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            /* result from following looks confusing.
10817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * for example: 10 10:10 - 11 10:10, it is not
10827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * clear that the first 10 is the 10th day
10837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            time.insert(0, 'd');
10847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            genFallbackPattern(Calendar.DATE, time);
10857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            time.insert(0, 'M');
10867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            genFallbackPattern(Calendar.MONTH, time);
10877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            time.insert(0, 'y');
10887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            genFallbackPattern(Calendar.YEAR, time);
10897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            */
10907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // prefix with yMd
10917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            timeSkeleton = DateFormat.YEAR_NUM_MONTH_DAY + timeSkeleton;
10927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String pattern =dtpng.getBestPattern(timeSkeleton);
10937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // for fall back interval patterns,
10947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // the first part of the pattern is empty,
10957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // the second part of the pattern is the full-pattern
10967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // should be used in fall-back.
10977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            PatternInfo ptn = new PatternInfo(
10987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    null, pattern, fInfo.getDefaultOrder());
10997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            intervalPatterns.put(DateIntervalInfo.
11007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.DATE], ptn);
11017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            intervalPatterns.put(DateIntervalInfo.
11027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.MONTH], ptn);
11037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            intervalPatterns.put(DateIntervalInfo.
11047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.YEAR], ptn);
11057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
11067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            /* if both present,
11077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * 1) when the year, month, or day differs,
11087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * concatenate the two original expressions with a separator between,
11097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * 2) otherwise, present the date followed by the
11107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * range expression for the time.
11117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             */
11127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            /*
11137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * 1) when the year, month, or day differs,
11147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * concatenate the two original expressions with a separator between,
11157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             */
11167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // if field exists, use fall back
11177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( !fieldExistsInSkeleton(Calendar.DATE, dateSkeleton) ) {
11187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // prefix skeleton with 'd'
11197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                skeleton = DateIntervalInfo.
11207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.DATE] + skeleton;
11217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                genFallbackPattern(Calendar.DATE, skeleton, intervalPatterns, dtpng);
11227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
11237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( !fieldExistsInSkeleton(Calendar.MONTH, dateSkeleton) ) {
11247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // then prefix skeleton with 'M'
11257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                skeleton = DateIntervalInfo.
11267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.MONTH] + skeleton;
11277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                genFallbackPattern(Calendar.MONTH, skeleton, intervalPatterns, dtpng);
11287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
11297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( !fieldExistsInSkeleton(Calendar.YEAR, dateSkeleton) ) {
11307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // then prefix skeleton with 'y'
11317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                skeleton = DateIntervalInfo.
11327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.YEAR] + skeleton;
11337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                genFallbackPattern(Calendar.YEAR, skeleton, intervalPatterns, dtpng);
11347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
11357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            /*
11377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * 2) otherwise, present the date followed by the
11387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * range expression for the time.
11397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             */
11407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Need the Date/Time pattern for concatnation the date with
11417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // the time interval.
11427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // The date/time pattern ( such as {0} {1} ) is saved in
11437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // calendar, that is why need to get the CalendarData here.
11447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            CalendarData calData = new CalendarData(locale, null);
11457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String[] patterns = calData.getDateTimePatterns();
11467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String datePattern =dtpng.getBestPattern(dateSkeleton);
11477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            concatSingleDate2TimeInterval(patterns[8], datePattern, Calendar.AM_PM, intervalPatterns);
11487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            concatSingleDate2TimeInterval(patterns[8], datePattern, Calendar.HOUR, intervalPatterns);
11497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            concatSingleDate2TimeInterval(patterns[8], datePattern, Calendar.MINUTE, intervalPatterns);
11507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
11517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return intervalPatterns;
11537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
11547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
11577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Generate fall back interval pattern given a calendar field,
11587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * a skeleton, and a date time pattern generator
11597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param field      the largest different calendar field
11607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton   a skeleton
11617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param dtpng      date time pattern generator
11627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param intervalPatterns interval patterns
11637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
11647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void genFallbackPattern(int field, String skeleton,
11657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    Map<String, PatternInfo> intervalPatterns,
11667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    DateTimePatternGenerator dtpng) {
11677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String pattern = dtpng.getBestPattern(skeleton);
11687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // for fall back interval patterns,
11697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // the first part of the pattern is empty,
11707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // the second part of the pattern is the full-pattern
11717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // should be used in fall-back.
11727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PatternInfo ptn = new PatternInfo(
11737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    null, pattern, fInfo.getDefaultOrder());
11747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        intervalPatterns.put(
11757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field], ptn);
11767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
11777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
11817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void genFallbackForNotFound(String field, StringBuffer skeleton) {
11827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( SimpleDateFormat.isFieldUnitIgnored(skeleton.toString(), field) ) {
11837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // single date
11847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            DateIntervalInfo.PatternInfo ptnInfo =
11857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                new DateIntervalInfo.PatternInfo(null, fDateFormat.toPattern(),
11867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                 fInfo.getDefaultOrder());
11877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fIntervalPatterns.put(field, ptnInfo);
11887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return;
11897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( skeleton.indexOf(field) == -1 ) {
11907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            skeleton.insert(0,field);
11917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            genFallbackPattern(field, skeleton, dtpng);
11927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
11937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
11947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    */
11957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
11977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * get separated date and time skeleton from a combined skeleton.
11987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
11997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The difference between date skeleton and normalizedDateSkeleton are:
12007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 1. both 'y' and 'd' are appeared only once in normalizeDateSkeleton
12017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 2. 'E' and 'EE' are normalized into 'EEE'
12027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 3. 'MM' is normalized into 'M'
12037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
12047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     ** the difference between time skeleton and normalizedTimeSkeleton are:
12057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 1. both 'H' and 'h' are normalized as 'h' in normalized time skeleton,
12067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 2. 'a' is omitted in normalized time skeleton.
12077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 3. there is only one appearance for 'h', 'm','v', 'z' in normalized time
12087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    skeleton
12097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
12107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
12117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  @param skeleton               given combined skeleton.
12127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  @param date                   Output parameter for date only skeleton.
12137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  @param normalizedDate         Output parameter for normalized date only
12147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
12157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  @param time                   Output parameter for time only skeleton.
12167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  @param normalizedTime         Output parameter for normalized time only
12177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                                skeleton.
12187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
12197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static void getDateTimeSkeleton(String skeleton,
12207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            StringBuilder dateSkeleton,
12217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            StringBuilder normalizedDateSkeleton,
12227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            StringBuilder timeSkeleton,
12237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            StringBuilder normalizedTimeSkeleton)
12247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
12257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // dateSkeleton follows the sequence of y*M*E*d*
12267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // timeSkeleton follows the sequence of hm*[v|z]?
12277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int i;
12287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int ECount = 0;
12297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int dCount = 0;
12307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int MCount = 0;
12317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int yCount = 0;
12327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int hCount = 0;
12337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int HCount = 0;
12347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int mCount = 0;
12357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int vCount = 0;
12367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int zCount = 0;
12377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (i = 0; i < skeleton.length(); ++i) {
12397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            char ch = skeleton.charAt(i);
12407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            switch ( ch ) {
12417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'E':
12427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateSkeleton.append(ch);
12437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++ECount;
12447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
12457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'd':
12467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateSkeleton.append(ch);
12477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++dCount;
12487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
12497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'M':
12507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateSkeleton.append(ch);
12517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++MCount;
12527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
12537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'y':
12547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateSkeleton.append(ch);
12557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++yCount;
12567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
12577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'G':
12587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'Y':
12597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'u':
12607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'Q':
12617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'q':
12627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'L':
12637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'l':
12647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'W':
12657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'w':
12667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'D':
12677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'F':
12687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'g':
12697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'e':
12707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'c':
1271f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert              case 'U':
1272f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert              case 'r':
12737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                normalizedDateSkeleton.append(ch);
12747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateSkeleton.append(ch);
12757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
12767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'a':
12777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // 'a' is implicitly handled
12787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                timeSkeleton.append(ch);
12797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
12807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'h':
12817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                timeSkeleton.append(ch);
12827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++hCount;
12837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
12847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'H':
12857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                timeSkeleton.append(ch);
12867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++HCount;
12877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
12887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'm':
12897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                timeSkeleton.append(ch);
12907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++mCount;
12917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
12927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'z':
12937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++zCount;
12947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                timeSkeleton.append(ch);
12957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
12967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'v':
12977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++vCount;
12987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                timeSkeleton.append(ch);
12997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
13007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'V':
13017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'Z':
13027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'k':
13037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'K':
13047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'j':
13057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 's':
13067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'S':
13077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'A':
13087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                timeSkeleton.append(ch);
13097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                normalizedTimeSkeleton.append(ch);
13107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
13117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
13127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /* generate normalized form for date*/
13157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( yCount != 0 ) {
13167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (i = 0; i < yCount; i++) {
13177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                normalizedDateSkeleton.append('y');
13187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
13197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( MCount != 0 ) {
13217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( MCount < 3 ) {
13227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                normalizedDateSkeleton.append('M');
13237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
13247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for ( i = 0; i < MCount && i < 5; ++i ) {
13257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                     normalizedDateSkeleton.append('M');
13267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
13277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
13287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( ECount != 0 ) {
13307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( ECount <= 3 ) {
13317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                normalizedDateSkeleton.append('E');
13327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
13337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for ( i = 0; i < ECount && i < 5; ++i ) {
13347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                     normalizedDateSkeleton.append('E');
13357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
13367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
13377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( dCount != 0 ) {
13397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            normalizedDateSkeleton.append('d');
13407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /* generate normalized form for time */
13437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( HCount != 0 ) {
13447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            normalizedTimeSkeleton.append('H');
13457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        else if ( hCount != 0 ) {
13477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            normalizedTimeSkeleton.append('h');
13487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( mCount != 0 ) {
13507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            normalizedTimeSkeleton.append('m');
13517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( zCount != 0 ) {
13537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            normalizedTimeSkeleton.append('z');
13547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( vCount != 0 ) {
13567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            normalizedTimeSkeleton.append('v');
13577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
13597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
13637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Generate date or time interval pattern from resource.
13647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
13657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * It needs to handle the following:
13667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 1. need to adjust field width.
13677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    For example, the interval patterns saved in DateIntervalInfo
13687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    includes "dMMMy", but not "dMMMMy".
13697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    Need to get interval patterns for dMMMMy from dMMMy.
13707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    Another example, the interval patterns saved in DateIntervalInfo
13717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    includes "hmv", but not "hmz".
13727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    Need to get interval patterns for "hmz' from 'hmv'
13737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
13747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 2. there might be no pattern for 'y' differ for skeleton "Md",
13757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    in order to get interval patterns for 'y' differ,
13767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    need to look for it from skeleton 'yMd'
13777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
13787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param dateSkeleton   normalized date skeleton
13797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param timeSkeleton   normalized time skeleton
13807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param intervalPatterns interval patterns
13817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return whether there is interval patterns for the skeleton.
13827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *         true if there is, false otherwise
13837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
13847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private boolean genSeparateDateTimePtn(String dateSkeleton,
13857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                           String timeSkeleton,
13867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                           Map<String, PatternInfo> intervalPatterns)
13877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
13887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String skeleton;
13897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // if both date and time skeleton present,
13907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // the final interval pattern might include time interval patterns
13917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // ( when, am_pm, hour, minute differ ),
13927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // but not date interval patterns ( when year, month, day differ ).
13937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // For year/month/day differ, it falls back to fall-back pattern.
13947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( timeSkeleton.length() != 0  ) {
13957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            skeleton = timeSkeleton;
13967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
13977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            skeleton = dateSkeleton;
13987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /* interval patterns for skeleton "dMMMy" (but not "dMMMMy")
14017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * are defined in resource,
14027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * interval patterns for skeleton "dMMMMy" are calculated by
14037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * 1. get the best match skeleton for "dMMMMy", which is "dMMMy"
14047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * 2. get the interval patterns for "dMMMy",
14057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * 3. extend "MMM" to "MMMM" in above interval patterns for "dMMMMy"
14067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * getBestSkeleton() is step 1.
14077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
14087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // best skeleton, and the difference information
14097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        BestMatchInfo retValue = fInfo.getBestSkeleton(skeleton);
14107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String bestSkeleton = retValue.bestMatchSkeleton;
14117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int differenceInfo =  retValue.bestMatchDistanceInfo;
14127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // difference:
14147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // 0 means the best matched skeleton is the same as input skeleton
14157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // 1 means the fields are the same, but field width are different
14167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // 2 means the only difference between fields are v/z,
14177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // -1 means there are other fields difference
14187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( differenceInfo == -1 ) {
14197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // skeleton has different fields, not only  v/z difference
14207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return false;
14217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( timeSkeleton.length() == 0 ) {
14247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // only has date skeleton
14257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            genIntervalPattern(Calendar.DATE, skeleton, bestSkeleton, differenceInfo, intervalPatterns);
14267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            SkeletonAndItsBestMatch skeletons = genIntervalPattern(
14277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                  Calendar.MONTH, skeleton,
14287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                  bestSkeleton, differenceInfo,
14297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                  intervalPatterns);
14307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( skeletons != null ) {
14317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                bestSkeleton = skeletons.skeleton;
14327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                skeleton = skeletons.bestMatchSkeleton;
14337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
14347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            genIntervalPattern(Calendar.YEAR, skeleton, bestSkeleton, differenceInfo, intervalPatterns);
14357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
14367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            genIntervalPattern(Calendar.MINUTE, skeleton, bestSkeleton, differenceInfo, intervalPatterns);
14377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            genIntervalPattern(Calendar.HOUR, skeleton, bestSkeleton, differenceInfo, intervalPatterns);
14387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            genIntervalPattern(Calendar.AM_PM, skeleton, bestSkeleton, differenceInfo, intervalPatterns);
14397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return true;
14417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
14437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
14477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Generate interval pattern from existing resource
14487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
14497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * It not only save the interval patterns,
14507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * but also return the skeleton and its best match skeleton.
14517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
14527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param field           largest different calendar field
14537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton        skeleton
14547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param bestSkeleton    the best match skeleton which has interval pattern
14557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                        defined in resource
14567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param differenceInfo  the difference between skeleton and best skeleton
14577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *         0 means the best matched skeleton is the same as input skeleton
14587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *         1 means the fields are the same, but field width are different
14597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *         2 means the only difference between fields are v/z,
14607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        -1 means there are other fields difference
14617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
14627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param intervalPatterns interval patterns
14637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
14647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return  an extended skeleton or extended best skeleton if applicable.
14657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *          null otherwise.
14667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
14677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private SkeletonAndItsBestMatch genIntervalPattern(
14687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                   int field, String skeleton, String bestSkeleton,
14697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                   int differenceInfo, Map<String, PatternInfo> intervalPatterns) {
14707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        SkeletonAndItsBestMatch retValue = null;
14717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PatternInfo pattern = fInfo.getIntervalPattern(
14727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                           bestSkeleton, field);
14737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( pattern == null ) {
14747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // single date
14757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( SimpleDateFormat.isFieldUnitIgnored(bestSkeleton, field) ) {
14767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                PatternInfo ptnInfo =
14777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    new PatternInfo(fDateFormat.toPattern(),
14787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                     null,
14797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                     fInfo.getDefaultOrder());
14807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                intervalPatterns.put(DateIntervalInfo.
14817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    CALENDAR_FIELD_TO_PATTERN_LETTER[field], ptnInfo);
14827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return null;
14837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
14847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // for 24 hour system, interval patterns in resource file
14867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // might not include pattern when am_pm differ,
14877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // which should be the same as hour differ.
14887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // add it here for simplicity
14897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( field == Calendar.AM_PM ) {
14907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 pattern = fInfo.getIntervalPattern(bestSkeleton,
14917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                         Calendar.HOUR);
14927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 if ( pattern != null ) {
14937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                      // share
14947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                      intervalPatterns.put(DateIntervalInfo.
14957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                          CALENDAR_FIELD_TO_PATTERN_LETTER[field],
14967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                          pattern);
14977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 }
14987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 return null;
14997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
15007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // else, looking for pattern when 'y' differ for 'dMMMM' skeleton,
15017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // first, get best match pattern "MMMd",
15027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // since there is no pattern for 'y' differs for skeleton 'MMMd',
15037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // need to look for it from skeleton 'yMMMd',
15047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // if found, adjust field width in interval pattern from
15057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // "MMM" to "MMMM".
15067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String fieldLetter =
15077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field];
15087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            bestSkeleton = fieldLetter + bestSkeleton;
15097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            skeleton = fieldLetter + skeleton;
15107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // for example, looking for patterns when 'y' differ for
15117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // skeleton "MMMM".
15127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            pattern = fInfo.getIntervalPattern(bestSkeleton, field);
15137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( pattern == null && differenceInfo == 0 ) {
15147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // if there is no skeleton "yMMMM" defined,
15157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // look for the best match skeleton, for example: "yMMM"
15167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                BestMatchInfo tmpRetValue = fInfo.getBestSkeleton(skeleton);
15177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String tmpBestSkeleton = tmpRetValue.bestMatchSkeleton;
15187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                differenceInfo =  tmpRetValue.bestMatchDistanceInfo;
15197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ( tmpBestSkeleton.length() != 0 && differenceInfo != -1 ) {
15207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    pattern = fInfo.getIntervalPattern(tmpBestSkeleton, field);
15217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    bestSkeleton = tmpBestSkeleton;
15227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
15237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
15247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( pattern != null ) {
15257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                retValue = new SkeletonAndItsBestMatch(skeleton, bestSkeleton);
15267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
15277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( pattern != null ) {
15297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( differenceInfo != 0 ) {
15307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String part1 = adjustFieldWidth(skeleton, bestSkeleton,
15317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                   pattern.getFirstPart(), differenceInfo);
15327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String part2 = adjustFieldWidth(skeleton, bestSkeleton,
15337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                   pattern.getSecondPart(), differenceInfo);
15347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                pattern =  new PatternInfo(part1, part2,
15357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                           pattern.firstDateInPtnIsLaterDate());
15367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
15377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // pattern is immutable, no need to clone;
15387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // pattern = (PatternInfo)pattern.clone();
15397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
15407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            intervalPatterns.put(
15417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field], pattern);
15427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return retValue;
15447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
15477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Adjust field width in best match interval pattern to match
15487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the field width in input skeleton.
15497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
15507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * TODO (xji) make a general solution
15517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The adjusting rule can be:
15527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 1. always adjust
15537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 2. never adjust
15547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 3. default adjust, which means adjust according to the following rules
15557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 3.1 always adjust string, such as MMM and MMMM
15567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 3.2 never adjust between string and numeric, such as MM and MMM
15577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 3.3 always adjust year
15587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 3.4 do not adjust 'd', 'h', or 'm' if h presents
15597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 3.5 do not adjust 'M' if it is numeric(?)
15607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
15617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Since date interval format is well-formed format,
15627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * date and time skeletons are normalized previously,
15637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * till this stage, the adjust here is only "adjust strings, such as MMM
15647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and MMMM, EEE and EEEE.
15657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
15667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param inputSkeleton            the input skeleton
15677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param bestMatchSkeleton        the best match skeleton
15687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param bestMatchIntervalpattern the best match interval pattern
15697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param differenceInfo           the difference between 2 skeletons
15707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                                 1 means only field width differs
15717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                                 2 means v/z exchange
15727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the adjusted interval pattern
15737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
15747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String adjustFieldWidth(String inputSkeleton,
15757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    String bestMatchSkeleton,
15767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    String bestMatchIntervalPattern,
15777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    int differenceInfo ) {
15787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( bestMatchIntervalPattern == null ) {
15807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null; // the 2nd part could be null
15817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int[] inputSkeletonFieldWidth = new int[58];
15837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int[] bestMatchSkeletonFieldWidth = new int[58];
15847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /* initialize as following
15867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {
15877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //       A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
15887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            0, 0, 0, 0, 0,  0, 0,  0,  0, 0, 0, 0, 0,  0, 0, 0,
15897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //   P   Q   R   S   T   U   V   W   X   Y   Z
15907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            0, 0, 0, 0, 0,  0, 0,  0,  0, 0, 0, 0, 0,  0, 0, 0,
15917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //       a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
15927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            0, 0, 0, 0, 0,  0, 0,  0,  0, 0, 0, 0, 0,  0, 0, 0,
15937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //   p   q   r   s   t   u   v   w   x   y   z
15947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            0, 0, 0, 0, 0,  0, 0,  0,  0, 0, 0, 0, 0,  0, 0, 0,
15957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        };
15967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        */
15977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DateIntervalInfo.parseSkeleton(inputSkeleton, inputSkeletonFieldWidth);
16007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DateIntervalInfo.parseSkeleton(bestMatchSkeleton, bestMatchSkeletonFieldWidth);
16017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( differenceInfo == 2 ) {
16027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            bestMatchIntervalPattern = bestMatchIntervalPattern.replace('v', 'z');
16037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder adjustedPtn = new StringBuilder(bestMatchIntervalPattern);
16067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean inQuote = false;
16087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        char prevCh = 0;
16097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int count = 0;
16107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int PATTERN_CHAR_BASE = 0x41;
16127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // loop through the pattern string character by character
16147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int adjustedPtnLength = adjustedPtn.length();
16157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < adjustedPtnLength; ++i) {
16167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            char ch = adjustedPtn.charAt(i);
16177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (ch != prevCh && count > 0) {
16187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // check the repeativeness of pattern letter
16197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                char skeletonChar = prevCh;
16207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ( skeletonChar == 'L' ) {
16217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // for skeleton "M+", the pattern is "...L..."
16227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    skeletonChar = 'M';
16237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
16247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int fieldCount = bestMatchSkeletonFieldWidth[skeletonChar - PATTERN_CHAR_BASE];
16257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int inputFieldCount = inputSkeletonFieldWidth[skeletonChar - PATTERN_CHAR_BASE];
16267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ( fieldCount == count && inputFieldCount > fieldCount ) {
16277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    count = inputFieldCount - fieldCount;
16287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    for ( int j = 0; j < count; ++j ) {
16297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        adjustedPtn.insert(i, prevCh);
16307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
16317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    i += count;
16327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    adjustedPtnLength += count;
16337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
16347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                count = 0;
16357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
16367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (ch == '\'') {
16377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Consecutive single quotes are a single quote literal,
16387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // either outside of quotes or between quotes
16397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ((i+1) < adjustedPtn.length() && adjustedPtn.charAt(i+1) == '\'') {
16407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ++i;
16417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
16427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    inQuote = ! inQuote;
16437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
16447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
16457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/)
16467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) {
16477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // ch is a date-time pattern character
16487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                prevCh = ch;
16497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++count;
16507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
16517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( count > 0 ) {
16537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // last item
16547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // check the repeativeness of pattern letter
16557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            char skeletonChar = prevCh;
16567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( skeletonChar == 'L' ) {
16577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // for skeleton "M+", the pattern is "...L..."
16587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                skeletonChar = 'M';
16597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
16607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int fieldCount = bestMatchSkeletonFieldWidth[skeletonChar - PATTERN_CHAR_BASE];
16617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int inputFieldCount = inputSkeletonFieldWidth[skeletonChar - PATTERN_CHAR_BASE];
16627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( fieldCount == count && inputFieldCount > fieldCount ) {
16637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                count = inputFieldCount - fieldCount;
16647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for ( int j = 0; j < count; ++j ) {
16657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    adjustedPtn.append(prevCh);
16667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
16677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
16687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return adjustedPtn.toString();
16707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
16717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
16747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Concat a single date pattern with a time interval pattern,
16757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * set it into the intervalPatterns, while field is time field.
16767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is used to handle time interval patterns on skeleton with
16777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * both time and date. Present the date followed by
16787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the range expression for the time.
16797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param dtfmt                  date and time format
16807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param datePattern            date pattern
16817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param field                  time calendar field: AM_PM, HOUR, MINUTE
16827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param intervalPatterns       interval patterns
16837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
16847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void concatSingleDate2TimeInterval(String dtfmt,
16857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                               String datePattern,
16867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                               int field,
16877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                               Map<String, PatternInfo> intervalPatterns)
16887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
16897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PatternInfo  timeItvPtnInfo =
16917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            intervalPatterns.get(DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field]);
16927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( timeItvPtnInfo != null ) {
16937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String timeIntervalPattern = timeItvPtnInfo.getFirstPart() +
16947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                         timeItvPtnInfo.getSecondPart();
16957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String pattern = MessageFormat.format(dtfmt, new Object[]
16967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                         {timeIntervalPattern, datePattern});
16977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            timeItvPtnInfo = DateIntervalInfo.genPatternInfo(pattern,
16987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                timeItvPtnInfo.firstDateInPtnIsLaterDate());
16997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            intervalPatterns.put(
17007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field], timeItvPtnInfo);
17017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
17027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // else: fall back
17037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // it should not happen if the interval format defined is valid
17047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
17057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
17087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * check whether a calendar field present in a skeleton.
17097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param field      calendar field need to check
17107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton   given skeleton on which to check the calendar field
17117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return           true if field present in a skeleton.
17127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
17137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static boolean fieldExistsInSkeleton(int field, String skeleton)
17147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
17157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String fieldChar = DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field];
17167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return ( (skeleton.indexOf(fieldChar) == -1) ? false : true ) ;
17177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
17187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
17217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * readObject.
17227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
17237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void readObject(ObjectInputStream stream)
17247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        throws IOException, ClassNotFoundException {
17257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        stream.defaultReadObject();
17267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        initializePattern(isDateIntervalInfoDefault ? LOCAL_PATTERN_CACHE : null);
17277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1728f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
1729f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    /**
1730f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * Get the internal patterns for the skeleton
1731f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @internal CLDR
1732f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @deprecated This API is ICU internal only.
1733f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     */
1734f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    @Deprecated
1735f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    public Map<String, PatternInfo> getRawPatterns() {
1736f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        // this is unmodifiable, so ok to return directly
1737f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        return fIntervalPatterns;
1738f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    }
17397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
1740