12d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// © 2016 and later: Unicode, Inc. and others.
22d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// License & terms of use: http://www.unicode.org/copyright.html#License
37935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/*
487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert*   Copyright (C) 2008-2016, International Business Machines
57935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert*   Corporation and others.  All Rights Reserved.
67935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert*/
77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.text;
97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.IOException;
117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.ObjectInputStream;
127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.FieldPosition;
137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.ParsePosition;
147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Collections;
157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.HashMap;
167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Locale;
177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Map;
187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.ICUCache;
202d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubertimport com.ibm.icu.impl.ICUData;
212d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubertimport com.ibm.icu.impl.ICUResourceBundle;
227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.SimpleCache;
2387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubertimport com.ibm.icu.impl.SimpleFormatterImpl;
247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.DateIntervalInfo.PatternInfo;
257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.Calendar;
267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.DateInterval;
277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.Output;
287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.TimeZone;
297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ULocale;
307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ULocale.Category;
312d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubertimport com.ibm.icu.util.UResourceBundle;
327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/**
357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * DateIntervalFormat is a class for formatting and parsing date
367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * intervals in a language-independent manner.
377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Only formatting is supported. Parsing is not supported.
387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Date interval means from one date to another date,
417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * for example, from "Jan 11, 2008" to "Jan 18, 2008".
427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * We introduced class DateInterval to represent it.
437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * DateInterval is a pair of UDate, which is
447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the standard milliseconds since 24:00 GMT, Jan 1, 1970.
457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * DateIntervalFormat formats a DateInterval into
487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * text as compactly as possible.
497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For example, the date interval format from "Jan 11, 2008" to "Jan 18,. 2008"
507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * is "Jan 11-18, 2008" for English.
517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * And it parses text into DateInterval,
527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * although initially, parsing is not supported.
537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * There is no structural information in date time patterns.
567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For any punctuations and string literals inside a date time pattern,
577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * we do not know whether it is just a separator, or a prefix, or a suffix.
587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Without such information, so, it is difficult to generate a sub-pattern
597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * (or super-pattern) by algorithm.
607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * So, formatting a DateInterval is pattern-driven. It is very
617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * similar to formatting in SimpleDateFormat.
627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * We introduce class DateIntervalInfo to save date interval
637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * patterns, similar to date time pattern in SimpleDateFormat.
647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Logically, the interval patterns are mappings
677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * from (skeleton, the_largest_different_calendar_field)
687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * to (date_interval_pattern).
697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * A skeleton
727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <ol>
737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>
747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * only keeps the field pattern letter and ignores all other parts
757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * in a pattern, such as space, punctuations, and string literals.
767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>
777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * hides the order of fields.
787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>
797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * might hide a field's pattern letter length.
807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For those non-digit calendar fields, the pattern letter length is
827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * important, such as MMM, MMMM, and MMMMM; EEE and EEEE,
837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and the field's pattern letter length is honored.
847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For the digit calendar fields,  such as M or MM, d or dd, yy or yyyy,
867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the field pattern length is ignored and the best match, which is defined
877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * in date time patterns, will be returned without honor the field pattern
887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * letter length in skeleton.
897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </ol>
907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The calendar fields we support for interval formatting are:
93bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert * year, month, date, day-of-week, am-pm, hour, hour-of-day, minute, and
94bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert * second (though we do not currently have specific intervalFormat data for
95bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert * skeletons with seconds).
967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Those calendar fields can be defined in the following order:
979e281ba4837cba4a1cf9523d6f8b0621b150063dScott Russell * year &gt; month &gt; date &gt; hour (in day) &gt; minute &gt; second
987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The largest different calendar fields between 2 calendars is the
1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * first different calendar field in above order.
1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For example: the largest different calendar fields between "Jan 10, 2007"
1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and "Feb 20, 2008" is year.
1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For other calendar fields, the compact interval formatting is not
1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * supported. And the interval format will be fall back to fall-back
1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * patterns, which is mostly "{date0} - {date1}".
1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * There is a set of pre-defined static skeleton strings in DateFormat,
1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * There are pre-defined interval patterns for those pre-defined skeletons
1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * in locales' resource files.
1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For example, for a skeleton YEAR_ABBR_MONTH_DAY, which is  "yMMMd",
1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * in  en_US, if the largest different calendar field between date1 and date2
1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * is "year", the date interval pattern  is "MMM d, yyyy - MMM d, yyyy",
1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * such as "Jan 10, 2007 - Jan 10, 2008".
1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If the largest different calendar field between date1 and date2 is "month",
1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the date interval pattern is "MMM d - MMM d, yyyy",
1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * such as "Jan 10 - Feb 10, 2007".
1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If the largest different calendar field between date1 and date2 is "day",
1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the date interval pattern is ""MMM d-d, yyyy", such as "Jan 10-20, 2007".
1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For date skeleton, the interval patterns when year, or month, or date is
1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * different are defined in resource files.
1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For time skeleton, the interval patterns when am/pm, or hour, or minute is
1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * different are defined in resource files.
1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If a skeleton is not found in a locale's DateIntervalInfo, which means
1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the interval patterns for the skeleton is not defined in resource file,
1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the interval pattern will falls back to the interval "fallback" pattern
1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * defined in resource file.
1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If the interval "fallback" pattern is not defined, the default fall-back
1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * is "{date0} - {data1}".
1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For the combination of date and time,
1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The rule to genearte interval patterns are:
1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <ol>
1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>
1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    when the year, month, or day differs, falls back to fall-back
1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    interval pattern, which mostly is the concatenate the two original
1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    expressions with a separator between,
1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    For example, interval pattern from "Jan 10, 2007 10:10 am"
1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    to "Jan 11, 2007 10:10am" is
1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    "Jan 10, 2007 10:10 am - Jan 11, 2007 10:10am"
1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>
1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    otherwise, present the date followed by the range expression
1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    for the time.
1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    For example, interval pattern from "Jan 10, 2007 10:10 am"
1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    to "Jan 10, 2007 11:10am" is "Jan 10, 2007 10:10 am - 11:10am"
1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </ol>
1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If two dates are the same, the interval pattern is the single date pattern.
1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For example, interval pattern from "Jan 10, 2007" to "Jan 10, 2007" is
1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * "Jan 10, 2007".
1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Or if the presenting fields between 2 dates have the exact same values,
1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the interval pattern is the  single date pattern.
1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For example, if user only requests year and month,
1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the interval pattern from "Jan 10, 2007" to "Jan 20, 2007" is "Jan 2007".
1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * DateIntervalFormat needs the following information for correct
1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * formatting: time zone, calendar type, pattern, date format symbols,
1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and date interval patterns.
1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * It can be instantiated in several ways:
1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <ol>
1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>
1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    create an instance using default or given locale plus given skeleton.
1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    Users are encouraged to created date interval formatter this way and
1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    to use the pre-defined skeleton macros, such as
1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    YEAR_NUM_MONTH, which consists the calendar fields and
1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    the format style.
1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </li>
1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>
1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    create an instance using default or given locale plus given skeleton
1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    plus a given DateIntervalInfo.
1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    This factory method is for powerful users who want to provide their own
1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    interval patterns.
1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    Locale provides the timezone, calendar, and format symbols information.
1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    Local plus skeleton provides full pattern information.
1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *    DateIntervalInfo provides the date interval patterns.
1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </li>
1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </ol>
1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For the calendar field pattern letter, such as G, y, M, d, a, h, H, m, s etc.
1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * DateIntervalFormat uses the same syntax as that of
1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * DateTime format.
1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Code Sample: general usage
1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <pre>
1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   // the date interval object which the DateIntervalFormat formats on
2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   // and parses into
2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   DateInterval dtInterval = new DateInterval(1000*3600*24L, 1000*3600*24*2L);
2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   DateIntervalFormat dtIntervalFmt = DateIntervalFormat.getInstance(
2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *                   YEAR_MONTH_DAY, Locale("en", "GB", ""));
2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   StringBuffer str = new StringBuffer("");
2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   FieldPosition pos = new FieldPosition(0);
2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   // formatting
2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   dtIntervalFmt.format(dtInterval, dateIntervalString, pos);
2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </pre>
2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
2127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Code Sample: for powerful users who wants to use their own interval pattern
2137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <pre>
2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     import com.ibm.icu.text.DateIntervalInfo;
2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     import com.ibm.icu.text.DateIntervalFormat;
2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     ....................
2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     // Get DateIntervalFormat instance using default locale
2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     DateIntervalFormat dtitvfmt = DateIntervalFormat.getInstance(YEAR_MONTH_DAY);
2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     // Create an empty DateIntervalInfo object, which does not have any interval patterns inside.
2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     dtitvinf = new DateIntervalInfo();
2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     // a series of set interval patterns.
226bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert *     // Only ERA, YEAR, MONTH, DATE,  DAY_OF_MONTH, DAY_OF_WEEK, AM_PM,  HOUR, HOUR_OF_DAY,
227bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert *     MINUTE and SECOND are supported.
2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     dtitvinf.setIntervalPattern("yMMMd", Calendar.YEAR, "'y ~ y'");
2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     dtitvinf.setIntervalPattern("yMMMd", Calendar.MONTH, "yyyy 'diff' MMM d - MMM d");
2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     dtitvinf.setIntervalPattern("yMMMd", Calendar.DATE, "yyyy MMM d ~ d");
2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     dtitvinf.setIntervalPattern("yMMMd", Calendar.HOUR_OF_DAY, "yyyy MMM d HH:mm ~ HH:mm");
2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     // Set fallback interval pattern. Fallback pattern is used when interval pattern is not found.
2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     // If the fall-back pattern is not set,  falls back to {date0} - {date1} if interval pattern is not found.
2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     dtitvinf.setFallbackIntervalPattern("{0} - {1}");
2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     // Set above DateIntervalInfo object as the interval patterns of date interval formatter
2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     dtitvfmt.setDateIntervalInfo(dtitvinf);
2397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     // Prepare to format
2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     pos = new FieldPosition(0);
2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     str = new StringBuffer("");
2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     // The 2 calendars should be equivalent, otherwise,  IllegalArgumentException will be thrown by format()
2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     Calendar fromCalendar = (Calendar) dtfmt.getCalendar().clone();
2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     Calendar toCalendar = (Calendar) dtfmt.getCalendar().clone();
2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     fromCalendar.setTimeInMillis(....);
2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     toCalendar.setTimeInMillis(...);
2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     //Formatting given 2 calendars
2517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *     dtitvfmt.format(fromCalendar, toCalendar, str, pos);
2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </pre>
25587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * <h3>Synchronization</h3>
25687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert *
25787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * The format methods of DateIntervalFormat may be used concurrently from multiple threads.
25887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * Functions that alter the state of a DateIntervalFormat object (setters)
25987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * may not be used concurrently with any other functions.
26087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert *
2617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.0
2627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
2637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic class DateIntervalFormat extends UFormat {
2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final long serialVersionUID = 1;
2677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Used to save the information for a skeleton's best match skeleton.
2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * It is package accessible since it is used in DateIntervalInfo too.
2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final class BestMatchInfo {
2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // the best match skeleton
2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final String bestMatchSkeleton;
2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // 0 means the best matched skeleton is the same as input skeleton
2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // 1 means the fields are the same, but field width are different
2777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // 2 means the only difference between fields are v/z,
2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // -1 means there are other fields difference
2797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final int    bestMatchDistanceInfo;
2807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        BestMatchInfo(String bestSkeleton, int difference) {
2817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            bestMatchSkeleton = bestSkeleton;
2827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            bestMatchDistanceInfo = difference;
2837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Used to save the information on a skeleton and its best match.
2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final class SkeletonAndItsBestMatch {
2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final String skeleton;
2927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final String bestMatchSkeleton;
2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        SkeletonAndItsBestMatch(String skeleton, String bestMatch) {
2947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.skeleton = skeleton;
2957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            bestMatchSkeleton = bestMatch;
2967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Cache for the locale interval pattern
3017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static ICUCache<String, Map<String, PatternInfo>> LOCAL_PATTERN_CACHE =
3027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        new SimpleCache<String, Map<String, PatternInfo>>();
3037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
3057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The interval patterns for this locale.
3067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private DateIntervalInfo     fInfo;
3087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
31087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     * The DateFormat object used to format single pattern.
31187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     * Because fDateFormat is modified during format operations, all
31287255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     * access to it from logically const, thread safe functions must be synchronized.
3137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private SimpleDateFormat     fDateFormat;
3157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
3177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The 2 calendars with the from and to date.
3187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * could re-use the calendar in fDateFormat,
3197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * but keeping 2 calendars make it clear and clean.
32087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     * Because these Calendars are modified during format operations, all
32187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert     * access to them from logically const, thread safe functions must be synchronized.
3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private Calendar fFromCalendar;
3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private Calendar fToCalendar;
3257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Following are transient interval information
328bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * relevant (locale) to this formatter.
3297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private String fSkeleton = null;
3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Needed for efficient deserialization. If set, it means we can use the
3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * cache to initialize fIntervalPatterns.
3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private boolean isDateIntervalInfoDefault;
3377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  Interval patterns for this instance's locale.
3407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private transient Map<String, PatternInfo> fIntervalPatterns = null;
342bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
343bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    /*
344bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * Patterns for fallback formatting.
345bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     */
346bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    private String fDatePattern = null;
347bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    private String fTimePattern = null;
348bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    private String fDateTimeFormat = null;
3497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
3527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * default constructor; private because we don't want anyone to use
3537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @SuppressWarnings("unused")
3557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private DateIntervalFormat() {
3567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct a DateIntervalFormat from DateFormat,
3607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * a DateIntervalInfo, and skeleton.
3617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * DateFormat provides the timezone, calendar,
3627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * full pattern, and date format symbols information.
3637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * It should be a SimpleDateFormat object which
3647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * has a pattern in it.
3657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the DateIntervalInfo provides the interval patterns.
3667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
3677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton  the skeleton of the date formatter
3687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param dtItvInfo  the DateIntervalInfo object to be adopted.
3697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param simpleDateFormat will be used for formatting
3707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
3717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
3727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
3737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
3757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public DateIntervalFormat(String skeleton, DateIntervalInfo dtItvInfo,
3767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                               SimpleDateFormat simpleDateFormat)
3777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
3787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fDateFormat = simpleDateFormat;
3797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // freeze date interval info
3807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        dtItvInfo.freeze();
3817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fSkeleton = skeleton;
3827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fInfo = dtItvInfo;
3837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        isDateIntervalInfoDefault = false;
3847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fFromCalendar = (Calendar) fDateFormat.getCalendar().clone();
3857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fToCalendar = (Calendar) fDateFormat.getCalendar().clone();
3867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        initializePattern(null);
3877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private DateIntervalFormat(String skeleton, ULocale locale,
3907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            SimpleDateFormat simpleDateFormat)
3917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
3927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fDateFormat = simpleDateFormat;
3937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fSkeleton = skeleton;
3947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fInfo = new DateIntervalInfo(locale).freeze();
3957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        isDateIntervalInfoDefault = true;
3967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fFromCalendar = (Calendar) fDateFormat.getCalendar().clone();
3977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fToCalendar = (Calendar) fDateFormat.getCalendar().clone();
3987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        initializePattern(LOCAL_PATTERN_CACHE);
3997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
4007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct a DateIntervalFormat from skeleton and  the default <code>FORMAT</code> locale.
4047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a convenient override of
4067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * getInstance(String skeleton, ULocale locale)
4077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * with the value of locale as default <code>FORMAT</code> locale.
4087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton  the skeleton on which interval format based.
4107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return          a date time interval formatter.
4117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see Category#FORMAT
4127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
4137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final DateIntervalFormat
4157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        getInstance(String skeleton)
4167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
4187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getInstance(skeleton, ULocale.getDefault(Category.FORMAT));
4197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct a DateIntervalFormat from skeleton and a given locale.
4247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a convenient override of
4267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * getInstance(String skeleton, ULocale locale)
4277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>Example code:{@.jcite com.ibm.icu.samples.text.dateintervalformat.DateIntervalFormatSample:---dtitvfmtPreDefinedExample}
4297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton  the skeleton on which interval format based.
4307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param locale    the given locale
4317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return          a date time interval formatter.
4327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
4337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final DateIntervalFormat
4357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        getInstance(String skeleton, Locale locale)
4367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
4377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getInstance(skeleton, ULocale.forLocale(locale));
4387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct a DateIntervalFormat from skeleton and a given locale.
4437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <P>
4447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * In this factory method,
4457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the date interval pattern information is load from resource files.
4467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Users are encouraged to created date interval formatter this way and
4477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * to use the pre-defined skeleton macros.
4487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <P>
4507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * There are pre-defined skeletons in DateFormat,
4517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * such as MONTH_DAY, YEAR_MONTH_WEEKDAY_DAY etc.
4527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Those skeletons have pre-defined interval patterns in resource files.
4547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Users are encouraged to use them.
4557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * For example:
4567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * DateIntervalFormat.getInstance(DateFormat.MONTH_DAY, false, loc);
4577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The given Locale provides the interval patterns.
4597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * For example, for en_GB, if skeleton is YEAR_ABBR_MONTH_WEEKDAY_DAY,
4607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * which is "yMMMEEEd",
4617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the interval patterns defined in resource file to above skeleton are:
4627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "EEE, d MMM, yyyy - EEE, d MMM, yyyy" for year differs,
4637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "EEE, d MMM - EEE, d MMM, yyyy" for month differs,
4647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "EEE, d - EEE, d MMM, yyyy" for day differs,
4657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton  the skeleton on which interval format based.
4667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param locale    the given locale
4677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return          a date time interval formatter.
4687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
4697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final DateIntervalFormat
4717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        getInstance(String skeleton, ULocale locale)
4727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
4737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DateTimePatternGenerator generator = DateTimePatternGenerator.getInstance(locale);
4747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new DateIntervalFormat(skeleton, locale, new SimpleDateFormat(generator.getBestPattern(skeleton), locale));
4757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct a DateIntervalFormat from skeleton
4817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  DateIntervalInfo, and the default <code>FORMAT</code> locale.
4827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a convenient override of
4847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * getInstance(String skeleton, ULocale locale, DateIntervalInfo dtitvinf)
4857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * with the locale value as default <code>FORMAT</code> locale.
4867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton  the skeleton on which interval format based.
4887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param dtitvinf  the DateIntervalInfo object to be adopted.
4897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return          a date time interval formatter.
4907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see Category#FORMAT
4917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
4927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final DateIntervalFormat getInstance(String skeleton,
4947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                   DateIntervalInfo dtitvinf)
4957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
4967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getInstance(skeleton, ULocale.getDefault(Category.FORMAT), dtitvinf);
4977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct a DateIntervalFormat from skeleton
5037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * a DateIntervalInfo, and the given locale.
5047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a convenient override of
5067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * getInstance(String skeleton, ULocale locale, DateIntervalInfo dtitvinf)
5077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>Example code:{@.jcite com.ibm.icu.samples.text.dateintervalformat.DateIntervalFormatSample:---dtitvfmtCustomizedExample}
5097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton  the skeleton on which interval format based.
5107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param locale    the given locale
5117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param dtitvinf  the DateIntervalInfo object to be adopted.
5127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return          a date time interval formatter.
5137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
5147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final DateIntervalFormat getInstance(String skeleton,
5167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                 Locale locale,
5177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                 DateIntervalInfo dtitvinf)
5187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
5197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return getInstance(skeleton, ULocale.forLocale(locale), dtitvinf);
5207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct a DateIntervalFormat from skeleton
5267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * a DateIntervalInfo, and the given locale.
5277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <P>
5297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * In this factory method, user provides its own date interval pattern
5307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * information, instead of using those pre-defined data in resource file.
5317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This factory method is for powerful users who want to provide their own
5327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * interval patterns.
5337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <P>
5357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * There are pre-defined skeleton in DateFormat,
5367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * such as MONTH_DAY, YEAR_MONTH_WEEKDAY_DAY etc.
5377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Those skeletons have pre-defined interval patterns in resource files.
5397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Users are encouraged to use them.
5407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * For example:
5417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * DateIntervalFormat.getInstance(DateFormat.MONTH_DAY, false, loc,itvinf);
5427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the DateIntervalInfo provides the interval patterns.
5447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * User are encouraged to set default interval pattern in DateIntervalInfo
5467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * as well, if they want to set other interval patterns ( instead of
5477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * reading the interval patterns from resource files).
5487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * When the corresponding interval pattern for a largest calendar different
5497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * field is not found ( if user not set it ), interval format fallback to
5507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the default interval pattern.
5517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If user does not provide default interval pattern, it fallback to
5527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * "{date0} - {date1}"
5537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton  the skeleton on which interval format based.
5557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param locale    the given locale
5567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param dtitvinf  the DateIntervalInfo object to be adopted.
5577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return          a date time interval formatter.
5587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
5597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final DateIntervalFormat getInstance(String skeleton,
5617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                 ULocale locale,
5627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                 DateIntervalInfo dtitvinf)
5637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
5647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // clone. If it is frozen, clone returns itself, otherwise, clone
5657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // returns a copy.
5667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        dtitvinf = (DateIntervalInfo)dtitvinf.clone();
5677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DateTimePatternGenerator generator = DateTimePatternGenerator.getInstance(locale);
5687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new DateIntervalFormat(skeleton, dtitvinf, new SimpleDateFormat(generator.getBestPattern(skeleton), locale));
5697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Clone this Format object polymorphically.
5747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return    A copy of the object.
5757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
5767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
57787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert    public synchronized Object clone()
5787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
5797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DateIntervalFormat other = (DateIntervalFormat) super.clone();
5807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        other.fDateFormat = (SimpleDateFormat) fDateFormat.clone();
5817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        other.fInfo = (DateIntervalInfo) fInfo.clone();
5827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        other.fFromCalendar = (Calendar) fFromCalendar.clone();
5837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        other.fToCalendar = (Calendar) fToCalendar.clone();
584bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        other.fDatePattern = fDatePattern;
585bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        other.fTimePattern = fTimePattern;
586bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        other.fDateTimeFormat = fDateTimeFormat;
5877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return other;
5887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Format an object to produce a string. This method handles Formattable
5937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * objects with a DateInterval type.
5947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If a the Formattable object type is not a DateInterval,
5957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * IllegalArgumentException is thrown.
5967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param obj               The object to format.
5987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          Must be a DateInterval.
5997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param appendTo          Output parameter to receive result.
6007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          Result is appended to existing contents.
6017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param fieldPosition     On input: an alignment field, if desired.
6027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          On output: the offsets of the alignment field.
603bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     *                          There may be multiple instances of a given field type
604bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     *                          in an interval format; in this case the fieldPosition
605bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     *                          offsets refer to the first instance.
6067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return                  Reference to 'appendTo' parameter.
6077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws    IllegalArgumentException  if the formatted object is not
6087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                                      DateInterval object
6097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
6107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
6117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public final StringBuffer
6127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        format(Object obj, StringBuffer appendTo, FieldPosition fieldPosition)
6137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
6147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( obj instanceof DateInterval ) {
6157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return format( (DateInterval)obj, appendTo, fieldPosition);
6167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        else {
6187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException("Cannot format given Object (" + obj.getClass().getName() + ") as a DateInterval");
6197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
6237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Format a DateInterval to produce a string.
6247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
6257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param dtInterval        DateInterval to be formatted.
6267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param appendTo          Output parameter to receive result.
6277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          Result is appended to existing contents.
6287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param fieldPosition     On input: an alignment field, if desired.
6297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          On output: the offsets of the alignment field.
630bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     *                          There may be multiple instances of a given field type
631bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     *                          in an interval format; in this case the fieldPosition
632bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     *                          offsets refer to the first instance.
6337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return                  Reference to 'appendTo' parameter.
6347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
6357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
63687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert    public final synchronized StringBuffer format(DateInterval dtInterval,
6377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                     StringBuffer appendTo,
6387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                     FieldPosition fieldPosition)
6397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
6407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fFromCalendar.setTimeInMillis(dtInterval.getFromDate());
6417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fToCalendar.setTimeInMillis(dtInterval.getToDate());
6427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return format(fFromCalendar, fToCalendar, appendTo, fieldPosition);
6437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
6467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
6477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
6487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
6497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
6507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getPatterns(Calendar fromCalendar,
6517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Calendar toCalendar,
6527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Output<String> part2) {
6537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // First, find the largest different calendar field.
6547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int field;
6557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( fromCalendar.get(Calendar.ERA) != toCalendar.get(Calendar.ERA) ) {
6567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.ERA;
6577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.YEAR) !=
6587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.YEAR) ) {
6597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.YEAR;
6607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.MONTH) !=
6617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.MONTH) ) {
6627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.MONTH;
6637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.DATE) !=
6647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.DATE) ) {
6657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.DATE;
6667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.AM_PM) !=
6677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.AM_PM) ) {
6687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.AM_PM;
6697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.HOUR) !=
6707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.HOUR) ) {
6717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.HOUR;
6727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.MINUTE) !=
6737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.MINUTE) ) {
6747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.MINUTE;
675bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        } else if ( fromCalendar.get(Calendar.SECOND) !=
676bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    toCalendar.get(Calendar.SECOND) ) {
677bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            field = Calendar.SECOND;
6787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
6797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null;
6807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PatternInfo intervalPattern = fIntervalPatterns.get(
6827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field]);
6837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        part2.value = intervalPattern.getSecondPart();
6847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return intervalPattern.getFirstPart();
6857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
6877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Format 2 Calendars to produce a string.
6887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
6897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param fromCalendar      calendar set to the from date in date interval
6907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          to be formatted into date interval string
6917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param toCalendar        calendar set to the to date in date interval
6927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          to be formatted into date interval string
6937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param appendTo          Output parameter to receive result.
6947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          Result is appended to existing contents.
6957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param pos               On input: an alignment field, if desired.
6967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          On output: the offsets of the alignment field.
697bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     *                          There may be multiple instances of a given field type
698bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     *                          in an interval format; in this case the fieldPosition
699bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     *                          offsets refer to the first instance.
7007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return                  Reference to 'appendTo' parameter.
7017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws    IllegalArgumentException  if the two calendars are not equivalent.
7027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
7037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
70487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert    public final synchronized StringBuffer format(Calendar fromCalendar,
7057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                     Calendar toCalendar,
7067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                     StringBuffer appendTo,
7077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                     FieldPosition pos)
7087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
7097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // not support different calendar types and time zones
7107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( !fromCalendar.isEquivalentTo(toCalendar) ) {
7117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException("can not format on two different calendars");
7127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // First, find the largest different calendar field.
7157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int field = -1; //init with an invalid value.
7167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( fromCalendar.get(Calendar.ERA) != toCalendar.get(Calendar.ERA) ) {
7187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.ERA;
7197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.YEAR) !=
7207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.YEAR) ) {
7217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.YEAR;
7227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.MONTH) !=
7237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.MONTH) ) {
7247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.MONTH;
7257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.DATE) !=
7267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.DATE) ) {
7277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.DATE;
7287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.AM_PM) !=
7297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.AM_PM) ) {
7307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.AM_PM;
7317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.HOUR) !=
7327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.HOUR) ) {
7337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.HOUR;
7347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( fromCalendar.get(Calendar.MINUTE) !=
7357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    toCalendar.get(Calendar.MINUTE) ) {
7367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            field = Calendar.MINUTE;
737bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert         } else if ( fromCalendar.get(Calendar.SECOND) !=
738bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    toCalendar.get(Calendar.SECOND) ) {
739bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            field = Calendar.SECOND;
740bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert       } else {
741bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            /* ignore the millisecond etc. small fields' difference.
7427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * use single date when all the above are the same.
7437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             */
7447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return fDateFormat.format(fromCalendar, appendTo, pos);
7457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
746bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        boolean fromToOnSameDay = (field==Calendar.AM_PM || field==Calendar.HOUR || field==Calendar.MINUTE || field==Calendar.SECOND);
7477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // get interval pattern
7497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PatternInfo intervalPattern = fIntervalPatterns.get(
7507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field]);
7517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( intervalPattern == null ) {
7537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( fDateFormat.isFieldUnitIgnored(field) ) {
7547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                /* the largest different calendar field is small than
7557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 * the smallest calendar field in pattern,
7567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 * return single date format.
7577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 */
7587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return fDateFormat.format(fromCalendar, appendTo, pos);
7597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
761bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            return fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, pos);
7627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // If the first part in interval pattern is empty,
7657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // the 2nd part of it saves the full-pattern used in fall-back.
7667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // For a 'real' interval pattern, the first part will never be empty.
7677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( intervalPattern.getFirstPart() == null ) {
7687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // fall back
769bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            return fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, pos,
7707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    intervalPattern.getSecondPart());
7717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Calendar firstCal;
7737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Calendar secondCal;
7747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( intervalPattern.firstDateInPtnIsLaterDate() ) {
7757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            firstCal = toCalendar;
7767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            secondCal = fromCalendar;
7777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
7787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            firstCal = fromCalendar;
7797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            secondCal = toCalendar;
7807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // break the interval pattern into 2 parts
7827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // first part should not be empty,
7837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String originalPattern = fDateFormat.toPattern();
7847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fDateFormat.applyPattern(intervalPattern.getFirstPart());
7857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fDateFormat.format(firstCal, appendTo, pos);
7867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( intervalPattern.getSecondPart() != null ) {
7877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fDateFormat.applyPattern(intervalPattern.getSecondPart());
788bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            FieldPosition otherPos = new FieldPosition(pos.getField());
789bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            fDateFormat.format(secondCal, appendTo, otherPos);
790bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            if (pos.getEndIndex() == 0 && otherPos.getEndIndex() > 0) {
7912d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                pos.setBeginIndex(otherPos.getBeginIndex());
7922d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                pos.setEndIndex(otherPos.getEndIndex());
793bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
7947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fDateFormat.applyPattern(originalPattern);
7967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return appendTo;
7977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
799bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    private void adjustPosition(String combiningPattern, // has {0} and {1} in it
800bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                String pat0, FieldPosition pos0, // pattern and pos corresponding to {0}
801bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                String pat1, FieldPosition pos1, // pattern and pos corresponding to {1}
802bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                FieldPosition posResult) {
803bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        int index0 = combiningPattern.indexOf("{0}");
804bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        int index1 = combiningPattern.indexOf("{1}");
805bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (index0 < 0 || index1 < 0) {
806bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            return;
807bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
808bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        int placeholderLen = 3; // length of "{0}" or "{1}"
809bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (index0 < index1) {
810bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            if (pos0.getEndIndex() > 0) {
811bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                posResult.setBeginIndex(pos0.getBeginIndex() + index0);
812bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                posResult.setEndIndex(pos0.getEndIndex() + index0);
813bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            } else if (pos1.getEndIndex() > 0) {
814bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                // here index1 >= 3
815bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                index1 += pat0.length() - placeholderLen; // adjust for pat0 replacing {0}
816bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                posResult.setBeginIndex(pos1.getBeginIndex() + index1);
817bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                posResult.setEndIndex(pos1.getEndIndex() + index1);
818bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
819bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        } else {
820bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            if (pos1.getEndIndex() > 0) {
821bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                posResult.setBeginIndex(pos1.getBeginIndex() + index1);
822bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                posResult.setEndIndex(pos1.getEndIndex() + index1);
823bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            } else if (pos0.getEndIndex() > 0) {
824bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                // here index0 >= 3
825bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                index0 += pat1.length() - placeholderLen; // adjust for pat1 replacing {1}
826bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                posResult.setBeginIndex(pos0.getBeginIndex() + index0);
827bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                posResult.setEndIndex(pos0.getEndIndex() + index0);
828bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
829bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
830bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    }
8317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
8337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Format 2 Calendars to using fall-back interval pattern
8347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
8357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The full pattern used in this fall-back format is the
8367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * full pattern of the date formatter.
8377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
8387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param fromCalendar      calendar set to the from date in date interval
8397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          to be formatted into date interval string
8407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param toCalendar        calendar set to the to date in date interval
8417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          to be formatted into date interval string
8427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param appendTo          Output parameter to receive result.
8437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          Result is appended to existing contents.
8447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param pos               On input: an alignment field, if desired.
8457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          On output: the offsets of the alignment field.
8467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return                  Reference to 'appendTo' parameter.
8477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private final StringBuffer fallbackFormat(Calendar fromCalendar,
8497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                              Calendar toCalendar,
850bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                              boolean fromToOnSameDay,
8517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                              StringBuffer appendTo,
8527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                              FieldPosition pos)  {
853bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            String fullPattern = null; // for saving the pattern in fDateFormat
854bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            boolean formatDatePlusTimeRange = (fromToOnSameDay && fDatePattern != null && fTimePattern != null);
8557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // the fall back
856bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            if (formatDatePlusTimeRange) {
857bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                fullPattern = fDateFormat.toPattern(); // save current pattern, restore later
858bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                fDateFormat.applyPattern(fTimePattern);
859bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
860bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            FieldPosition otherPos = new FieldPosition(pos.getField());
8617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            StringBuffer earlierDate = new StringBuffer(64);
8627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            earlierDate = fDateFormat.format(fromCalendar, earlierDate, pos);
8637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            StringBuffer laterDate = new StringBuffer(64);
864bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            laterDate = fDateFormat.format(toCalendar, laterDate, otherPos);
8657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String fallbackPattern = fInfo.getFallbackIntervalPattern();
866bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            adjustPosition(fallbackPattern, earlierDate.toString(), pos, laterDate.toString(), otherPos, pos);
86787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert            String fallbackRange = SimpleFormatterImpl.formatRawPattern(
86887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert                    fallbackPattern, 2, 2, earlierDate, laterDate);
869bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            if (formatDatePlusTimeRange) {
870bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                // fallbackRange has just the time range, need to format the date part and combine that
871bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                fDateFormat.applyPattern(fDatePattern);
872bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                StringBuffer datePortion = new StringBuffer(64);
873bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                otherPos.setBeginIndex(0);
874bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                otherPos.setEndIndex(0);
875bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                datePortion = fDateFormat.format(fromCalendar, datePortion, otherPos);
876bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                adjustPosition(fDateTimeFormat, fallbackRange, pos, datePortion.toString(), otherPos, pos);
87787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert                fallbackRange = SimpleFormatterImpl.formatRawPattern(
87887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert                        fDateTimeFormat, 2, 2, fallbackRange, datePortion);
879bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
880bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            appendTo.append(fallbackRange);
881bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            if (formatDatePlusTimeRange) {
882bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                // restore full pattern
883bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                fDateFormat.applyPattern(fullPattern);
884bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
8857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return appendTo;
8867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
8907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Format 2 Calendars to using fall-back interval pattern
8917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
8927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This fall-back pattern is generated on a given full pattern,
8937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * not the full pattern of the date formatter.
8947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
8957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param fromCalendar      calendar set to the from date in date interval
8967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          to be formatted into date interval string
8977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param toCalendar        calendar set to the to date in date interval
8987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          to be formatted into date interval string
8997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param appendTo          Output parameter to receive result.
9007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          Result is appended to existing contents.
9017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param pos               On input: an alignment field, if desired.
9027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                          On output: the offsets of the alignment field.
9037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param fullPattern       the full pattern need to apply to date formatter
9047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return                  Reference to 'appendTo' parameter.
9057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private final StringBuffer fallbackFormat(Calendar fromCalendar,
9077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                              Calendar toCalendar,
908bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                              boolean fromToOnSameDay,
9097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                              StringBuffer appendTo,
9107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                              FieldPosition pos,
9117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                              String fullPattern)  {
9127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String originalPattern = fDateFormat.toPattern();
9137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fDateFormat.applyPattern(fullPattern);
914bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, pos);
9157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fDateFormat.applyPattern(originalPattern);
9167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return appendTo;
9177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Date interval parsing is not supported.
9227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <P>
9237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This method should handle parsing of
9247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * date time interval strings into Formattable objects with
9257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * DateInterval type, which is a pair of UDate.
9267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <P>
9277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Before calling, set parse_pos.index to the offset you want to start
9287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * parsing at in the source. After calling, parse_pos.index is the end of
9297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the text you parsed. If error occurs, index is unchanged.
9307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <P>
9317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * When parsing, leading whitespace is discarded (with a successful parse),
9327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * while trailing whitespace is left as is.
9337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <P>
9347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * See Format.parseObject() for more.
9357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
9367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param source    The string to be parsed into an object.
9377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param parse_pos The position to start parsing at. Since no parsing
9387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                  is supported, upon return this param is unchanged.
9397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return          A newly created Formattable* object, or NULL
9407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                  on failure.
9417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
9427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
9437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
9457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Object parseObject(String source, ParsePosition parse_pos)
9467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
9477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        throw new UnsupportedOperationException("parsing is not supported");
9487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Gets the date time interval patterns.
9537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return a copy of the date time interval patterns associated with
9547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * this date interval formatter.
9557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
9567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public DateIntervalInfo getDateIntervalInfo()
9587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
9597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return (DateIntervalInfo)fInfo.clone();
9607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Set the date time interval patterns.
9657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param newItvPattern   the given interval patterns to copy.
9667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
9677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void setDateIntervalInfo(DateIntervalInfo newItvPattern)
9697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
9707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // clone it. If it is frozen, the clone returns itself.
9717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Otherwise, clone returns a copy
9727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fInfo = (DateIntervalInfo)newItvPattern.clone();
9737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        this.isDateIntervalInfoDefault = false;
9747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fInfo.freeze(); // freeze it
9757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( fDateFormat != null ) {
9767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            initializePattern(null);
9777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Get the TimeZone
9827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return A copy of the TimeZone associated with this date interval formatter.
983f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @stable ICU 53
9847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public TimeZone getTimeZone()
9867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
9877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( fDateFormat != null ) {
9887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Here we clone, like other getters here, but unlike
9897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // DateFormat.getTimeZone() and Calendar.getTimeZone()
9907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // which return the TimeZone from the Calendar's zone variable
9917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return (TimeZone)(fDateFormat.getTimeZone().clone());
9927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // If fDateFormat is null (unexpected), return default timezone.
9947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return TimeZone.getDefault();
9957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Set the TimeZone for the calendar used by this DateIntervalFormat object.
10007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param zone The new TimeZone, will be cloned for use by this DateIntervalFormat.
1001f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @stable ICU 53
10027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
10037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void setTimeZone(TimeZone zone)
10047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
10057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // zone is cloned once for all three usages below:
10067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        TimeZone zoneToSet = (TimeZone)zone.clone();
10077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (fDateFormat != null) {
10087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fDateFormat.setTimeZone(zoneToSet);
10097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // fDateFormat has the master calendar for the DateIntervalFormat;
10117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // fFromCalendar and fToCalendar are internal work clones of that calendar.
10127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (fFromCalendar != null) {
10137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fFromCalendar.setTimeZone(zoneToSet);
10147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (fToCalendar != null) {
10167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fToCalendar.setTimeZone(zoneToSet);
10177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
10217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Gets the date formatter
10227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return a copy of the date formatter associated with
10237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * this date interval formatter.
10247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
10257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
102687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert    public synchronized DateFormat getDateFormat()
10277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
10287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return (DateFormat)fDateFormat.clone();
10297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
10337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  Below are for generating interval patterns locale to the formatter
10347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
10357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
10377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Initialize interval patterns locale to this formatter.
10387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
10397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void initializePattern(ICUCache<String, Map<String, PatternInfo>> cache) {
10407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String fullPattern = fDateFormat.toPattern();
10417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ULocale locale = fDateFormat.getLocale();
10427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String key = null;
10437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Map<String, PatternInfo> patterns = null;
10447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (cache != null) {
10457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( fSkeleton != null ) {
10467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                key = locale.toString() + "+" + fullPattern + "+" + fSkeleton;
10477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
10487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                key = locale.toString() + "+" + fullPattern;
10497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            patterns = cache.get(key);
10517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (patterns == null) {
10537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Map<String, PatternInfo> intervalPatterns = initializeIntervalPattern(fullPattern, locale);
10547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            patterns = Collections.unmodifiableMap(intervalPatterns);
10557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (cache != null) {
10567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                cache.put(key, patterns);
10577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fIntervalPatterns = patterns;
10607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
10657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Initialize interval patterns locale to this formatter
10667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
10677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This code is a bit complicated since
10687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 1. the interval patterns saved in resource bundle files are interval
10697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    patterns based on date or time only.
10707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    It does not have interval patterns based on both date and time.
10717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    Interval patterns on both date and time are algorithm generated.
10727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
10737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    For example, it has interval patterns on skeleton "dMy" and "hm",
10747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    but it does not have interval patterns on skeleton "dMyhm".
10757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
10767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    The rule to generate interval patterns for both date and time skeleton are
10777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    1) when the year, month, or day differs, concatenate the two original
10787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    expressions with a separator between,
10797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    For example, interval pattern from "Jan 10, 2007 10:10 am"
10807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    to "Jan 11, 2007 10:10am" is
10817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    "Jan 10, 2007 10:10 am - Jan 11, 2007 10:10am"
10827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
10837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    2) otherwise, present the date followed by the range expression
10847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    for the time.
10857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    For example, interval pattern from "Jan 10, 2007 10:10 am"
10867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    to "Jan 10, 2007 11:10am" is
10877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    "Jan 10, 2007 10:10 am - 11:10am"
10887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
10897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 2. even a pattern does not request a certain calendar field,
10907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    the interval pattern needs to include such field if such fields are
10917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    different between 2 dates.
10927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    For example, a pattern/skeleton is "hm", but the interval pattern
10937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    includes year, month, and date when year, month, and date differs.
10947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
10957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
10967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param fullPattern  formatter's full pattern
10977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param locale       the given locale.
10987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return             interval patterns' hash map
10997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
11007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private Map<String, PatternInfo> initializeIntervalPattern(String fullPattern, ULocale locale) {
11017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DateTimePatternGenerator dtpng = DateTimePatternGenerator.getInstance(locale);
11027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( fSkeleton == null ) {
11037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // fSkeleton is already set by getDateIntervalInstance()
11047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // or by getInstance(String skeleton, .... )
11057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fSkeleton = dtpng.getSkeleton(fullPattern);
11067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
11077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String skeleton = fSkeleton;
11087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        HashMap<String, PatternInfo> intervalPatterns = new HashMap<String, PatternInfo>();
11107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /* Check whether the skeleton is a combination of date and time.
11127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * For the complication reason 1 explained above.
11137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
11147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder date = new StringBuilder(skeleton.length());
11157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder normalizedDate = new StringBuilder(skeleton.length());
11167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder time = new StringBuilder(skeleton.length());
11177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder normalizedTime = new StringBuilder(skeleton.length());
11187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /* the difference between time skeleton and normalizedTimeSkeleton are:
11207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * 1. (Formerly, normalized time skeleton folded 'H' to 'h'; no longer true)
11217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * 2. 'a' is omitted in normalized time skeleton.
11227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * 3. there is only one appearance for 'h', 'm','v', 'z' in normalized
11237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *    time skeleton
11247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
11257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * The difference between date skeleton and normalizedDateSkeleton are:
11267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * 1. both 'y' and 'd' appear only once in normalizeDateSkeleton
11277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * 2. 'E' and 'EE' are normalized into 'EEE'
11287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * 3. 'MM' is normalized into 'M'
11297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
11307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        getDateTimeSkeleton(skeleton, date, normalizedDate,
11317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            time, normalizedTime);
11327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String dateSkeleton = date.toString();
11347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String timeSkeleton = time.toString();
11357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String normalizedDateSkeleton = normalizedDate.toString();
11367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String normalizedTimeSkeleton = normalizedTime.toString();
11377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1138bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        // move this up here since we need it for fallbacks
1139bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (time.length() != 0 && date.length() != 0) {
11402d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            // Need the Date/Time pattern for concatenating the date with
1141bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            // the time interval.
1142bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            // The date/time pattern ( such as {0} {1} ) is saved in
1143bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            // calendar, that is why need to get the CalendarData here.
11442d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            fDateTimeFormat = getConcatenationPattern(locale);
1145bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
1146bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
11477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean found = genSeparateDateTimePtn(normalizedDateSkeleton,
11487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                               normalizedTimeSkeleton,
1149bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                               intervalPatterns, dtpng);
11507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1151bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        // for skeletons with seconds, found is false and we enter this block
11527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( found == false ) {
11537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // use fallback
11547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // TODO: if user asks "m", but "d" differ
11557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //StringBuffer skeleton = new StringBuffer(skeleton);
11567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( time.length() != 0 ) {
11577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //genFallbackForNotFound(Calendar.MINUTE, skeleton);
11587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //genFallbackForNotFound(Calendar.HOUR, skeleton);
11597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //genFallbackForNotFound(Calendar.AM_PM, skeleton);
11607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ( date.length() == 0 ) {
11617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // prefix with yMd
11627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    timeSkeleton = DateFormat.YEAR_NUM_MONTH_DAY + timeSkeleton;
11637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    String pattern =dtpng.getBestPattern(timeSkeleton);
11647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // for fall back interval patterns,
11657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // the first part of the pattern is empty,
11667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // the second part of the pattern is the full-pattern
11677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // should be used in fall-back.
11687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    PatternInfo ptn = new PatternInfo(null, pattern,
11697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                     fInfo.getDefaultOrder());
11707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    intervalPatterns.put(DateIntervalInfo.
11717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.DATE], ptn);
11727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // share interval pattern
11737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    intervalPatterns.put(DateIntervalInfo.
11747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.MONTH], ptn);
11757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // share interval pattern
11767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    intervalPatterns.put(DateIntervalInfo.
11777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.YEAR], ptn);
11787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
11797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //genFallbackForNotFound(Calendar.DATE, skeleton);
11807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //genFallbackForNotFound(Calendar.MONTH, skeleton);
11817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //genFallbackForNotFound(Calendar.YEAR, skeleton);
11827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
11837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
11847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //genFallbackForNotFound(Calendar.DATE, skeleton);
11857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //genFallbackForNotFound(Calendar.MONTH, skeleton);
11867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //genFallbackForNotFound(Calendar.YEAR, skeleton);
11877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
11887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return intervalPatterns;
11897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } // end of skeleton not found
11907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // interval patterns for skeleton are found in resource
11917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( time.length() == 0 ) {
11927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // done
11937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( date.length() == 0 ) {
11947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // need to set up patterns for y/M/d differ
11957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            /* result from following looks confusing.
11967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * for example: 10 10:10 - 11 10:10, it is not
11977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * clear that the first 10 is the 10th day
11987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            time.insert(0, 'd');
11997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            genFallbackPattern(Calendar.DATE, time);
12007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            time.insert(0, 'M');
12017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            genFallbackPattern(Calendar.MONTH, time);
12027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            time.insert(0, 'y');
12037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            genFallbackPattern(Calendar.YEAR, time);
12047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            */
12057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // prefix with yMd
12067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            timeSkeleton = DateFormat.YEAR_NUM_MONTH_DAY + timeSkeleton;
12077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String pattern =dtpng.getBestPattern(timeSkeleton);
12087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // for fall back interval patterns,
12097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // the first part of the pattern is empty,
12107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // the second part of the pattern is the full-pattern
12117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // should be used in fall-back.
12127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            PatternInfo ptn = new PatternInfo(
12137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    null, pattern, fInfo.getDefaultOrder());
12147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            intervalPatterns.put(DateIntervalInfo.
12157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.DATE], ptn);
12167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            intervalPatterns.put(DateIntervalInfo.
12177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.MONTH], ptn);
12187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            intervalPatterns.put(DateIntervalInfo.
12197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.YEAR], ptn);
12207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
12217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            /* if both present,
12227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * 1) when the year, month, or day differs,
12237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * concatenate the two original expressions with a separator between,
12247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * 2) otherwise, present the date followed by the
12257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * range expression for the time.
12267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             */
12277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            /*
12287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * 1) when the year, month, or day differs,
12297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * concatenate the two original expressions with a separator between,
12307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             */
12317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // if field exists, use fall back
12327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( !fieldExistsInSkeleton(Calendar.DATE, dateSkeleton) ) {
12337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // prefix skeleton with 'd'
12347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                skeleton = DateIntervalInfo.
12357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.DATE] + skeleton;
12367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                genFallbackPattern(Calendar.DATE, skeleton, intervalPatterns, dtpng);
12377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
12387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( !fieldExistsInSkeleton(Calendar.MONTH, dateSkeleton) ) {
12397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // then prefix skeleton with 'M'
12407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                skeleton = DateIntervalInfo.
12417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.MONTH] + skeleton;
12427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                genFallbackPattern(Calendar.MONTH, skeleton, intervalPatterns, dtpng);
12437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
12447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( !fieldExistsInSkeleton(Calendar.YEAR, dateSkeleton) ) {
12457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // then prefix skeleton with 'y'
12467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                skeleton = DateIntervalInfo.
12477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.YEAR] + skeleton;
12487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                genFallbackPattern(Calendar.YEAR, skeleton, intervalPatterns, dtpng);
12497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
12507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            /*
12527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * 2) otherwise, present the date followed by the
12537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             * range expression for the time.
12547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert             */
1255bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            if (fDateTimeFormat == null) {
1256bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                fDateTimeFormat = "{1} {0}";
1257bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
12587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String datePattern =dtpng.getBestPattern(dateSkeleton);
1259bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            concatSingleDate2TimeInterval(fDateTimeFormat, datePattern, Calendar.AM_PM, intervalPatterns);
1260bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            concatSingleDate2TimeInterval(fDateTimeFormat, datePattern, Calendar.HOUR, intervalPatterns);
1261bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            concatSingleDate2TimeInterval(fDateTimeFormat, datePattern, Calendar.MINUTE, intervalPatterns);
12627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
12637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return intervalPatterns;
12657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
12667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12672d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    /**
12682d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     * Retrieves the concatenation DateTime pattern from the resource bundle.
12692d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     * @param locale Locale to retrieve.
12702d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     * @return Concatenation DateTime pattern.
12712d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert     */
12722d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    private String getConcatenationPattern(ULocale locale) {
12732d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        ICUResourceBundle rb = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, locale);
12742d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        ICUResourceBundle dtPatternsRb = rb.getWithFallback("calendar/gregorian/DateTimePatterns");
12752d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        ICUResourceBundle concatenationPatternRb = (ICUResourceBundle) dtPatternsRb.get(8);
12762d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        if (concatenationPatternRb.getType() == UResourceBundle.STRING) {
12772d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            return concatenationPatternRb.getString();
12782d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        } else {
12792d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            return concatenationPatternRb.getString(0);
12802d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        }
12812d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    }
12827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
12847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Generate fall back interval pattern given a calendar field,
12857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * a skeleton, and a date time pattern generator
12867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param field      the largest different calendar field
12877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton   a skeleton
12887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param dtpng      date time pattern generator
12897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param intervalPatterns interval patterns
12907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
12917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void genFallbackPattern(int field, String skeleton,
12927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    Map<String, PatternInfo> intervalPatterns,
12937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    DateTimePatternGenerator dtpng) {
12947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String pattern = dtpng.getBestPattern(skeleton);
12957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // for fall back interval patterns,
12967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // the first part of the pattern is empty,
12977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // the second part of the pattern is the full-pattern
12987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // should be used in fall-back.
12997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PatternInfo ptn = new PatternInfo(
13007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    null, pattern, fInfo.getDefaultOrder());
13017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        intervalPatterns.put(
13027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field], ptn);
13037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
13047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
13087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void genFallbackForNotFound(String field, StringBuffer skeleton) {
13097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( SimpleDateFormat.isFieldUnitIgnored(skeleton.toString(), field) ) {
13107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // single date
13117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            DateIntervalInfo.PatternInfo ptnInfo =
13127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                new DateIntervalInfo.PatternInfo(null, fDateFormat.toPattern(),
13137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                 fInfo.getDefaultOrder());
13147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fIntervalPatterns.put(field, ptnInfo);
13157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return;
13167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( skeleton.indexOf(field) == -1 ) {
13177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            skeleton.insert(0,field);
13187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            genFallbackPattern(field, skeleton, dtpng);
13197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
13207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
13217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    */
13227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
13247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * get separated date and time skeleton from a combined skeleton.
13257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
13267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The difference between date skeleton and normalizedDateSkeleton are:
13277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 1. both 'y' and 'd' are appeared only once in normalizeDateSkeleton
13287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 2. 'E' and 'EE' are normalized into 'EEE'
13297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 3. 'MM' is normalized into 'M'
13307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
13317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     ** the difference between time skeleton and normalizedTimeSkeleton are:
13327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 1. both 'H' and 'h' are normalized as 'h' in normalized time skeleton,
13337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 2. 'a' is omitted in normalized time skeleton.
13347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 3. there is only one appearance for 'h', 'm','v', 'z' in normalized time
13357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    skeleton
13367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
13377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
13387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  @param skeleton               given combined skeleton.
13397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  @param date                   Output parameter for date only skeleton.
13407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  @param normalizedDate         Output parameter for normalized date only
13417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
13427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  @param time                   Output parameter for time only skeleton.
13437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  @param normalizedTime         Output parameter for normalized time only
13447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                                skeleton.
13457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
13467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static void getDateTimeSkeleton(String skeleton,
13477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            StringBuilder dateSkeleton,
13487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            StringBuilder normalizedDateSkeleton,
13497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            StringBuilder timeSkeleton,
13507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            StringBuilder normalizedTimeSkeleton)
13517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
13527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // dateSkeleton follows the sequence of y*M*E*d*
13537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // timeSkeleton follows the sequence of hm*[v|z]?
13547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int i;
13557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int ECount = 0;
13567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int dCount = 0;
13577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int MCount = 0;
13587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int yCount = 0;
13597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int hCount = 0;
13607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int HCount = 0;
13617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int mCount = 0;
13627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int vCount = 0;
13637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int zCount = 0;
13647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (i = 0; i < skeleton.length(); ++i) {
13667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            char ch = skeleton.charAt(i);
13677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            switch ( ch ) {
13687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'E':
13697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateSkeleton.append(ch);
13707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++ECount;
13717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
13727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'd':
13737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateSkeleton.append(ch);
13747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++dCount;
13757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
13767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'M':
13777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateSkeleton.append(ch);
13787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++MCount;
13797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
13807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'y':
13817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateSkeleton.append(ch);
13827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++yCount;
13837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
13847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'G':
13857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'Y':
13867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'u':
13877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'Q':
13887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'q':
13897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'L':
13907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'l':
13917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'W':
13927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'w':
13937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'D':
13947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'F':
13957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'g':
13967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'e':
13977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'c':
1398f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert              case 'U':
1399f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert              case 'r':
14007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                normalizedDateSkeleton.append(ch);
14017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                dateSkeleton.append(ch);
14027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
14037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'a':
14047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // 'a' is implicitly handled
14057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                timeSkeleton.append(ch);
14067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
14077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'h':
14087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                timeSkeleton.append(ch);
14097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++hCount;
14107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
14117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'H':
14127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                timeSkeleton.append(ch);
14137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++HCount;
14147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
14157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'm':
14167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                timeSkeleton.append(ch);
14177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++mCount;
14187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
14197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'z':
14207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++zCount;
14217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                timeSkeleton.append(ch);
14227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
14237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'v':
14247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++vCount;
14257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                timeSkeleton.append(ch);
14267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
14277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'V':
14287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'Z':
14297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'k':
14307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'K':
14317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'j':
14327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 's':
14337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'S':
14347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              case 'A':
14357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                timeSkeleton.append(ch);
14367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                normalizedTimeSkeleton.append(ch);
14377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
14387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
14397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /* generate normalized form for date*/
14427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( yCount != 0 ) {
14437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (i = 0; i < yCount; i++) {
14447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                normalizedDateSkeleton.append('y');
14457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
14467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( MCount != 0 ) {
14487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( MCount < 3 ) {
14497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                normalizedDateSkeleton.append('M');
14507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
14517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for ( i = 0; i < MCount && i < 5; ++i ) {
14527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                     normalizedDateSkeleton.append('M');
14537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
14547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
14557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( ECount != 0 ) {
14577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( ECount <= 3 ) {
14587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                normalizedDateSkeleton.append('E');
14597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
14607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for ( i = 0; i < ECount && i < 5; ++i ) {
14617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                     normalizedDateSkeleton.append('E');
14627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
14637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
14647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( dCount != 0 ) {
14667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            normalizedDateSkeleton.append('d');
14677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /* generate normalized form for time */
14707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( HCount != 0 ) {
14717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            normalizedTimeSkeleton.append('H');
14727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        else if ( hCount != 0 ) {
14747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            normalizedTimeSkeleton.append('h');
14757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( mCount != 0 ) {
14777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            normalizedTimeSkeleton.append('m');
14787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( zCount != 0 ) {
14807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            normalizedTimeSkeleton.append('z');
14817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( vCount != 0 ) {
14837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            normalizedTimeSkeleton.append('v');
14847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
14857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
14867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
14897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
14907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Generate date or time interval pattern from resource.
14917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
14927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * It needs to handle the following:
14937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 1. need to adjust field width.
14947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    For example, the interval patterns saved in DateIntervalInfo
14957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    includes "dMMMy", but not "dMMMMy".
14967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    Need to get interval patterns for dMMMMy from dMMMy.
14977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    Another example, the interval patterns saved in DateIntervalInfo
14987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    includes "hmv", but not "hmz".
14997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    Need to get interval patterns for "hmz' from 'hmv'
15007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
15017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 2. there might be no pattern for 'y' differ for skeleton "Md",
15027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    in order to get interval patterns for 'y' differ,
15037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *    need to look for it from skeleton 'yMd'
15047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
15057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param dateSkeleton   normalized date skeleton
15067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param timeSkeleton   normalized time skeleton
15077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param intervalPatterns interval patterns
15087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return whether there is interval patterns for the skeleton.
15097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *         true if there is, false otherwise
15107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
15117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private boolean genSeparateDateTimePtn(String dateSkeleton,
15127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                           String timeSkeleton,
1513bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                           Map<String, PatternInfo> intervalPatterns,
1514bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                                           DateTimePatternGenerator dtpng)
15157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
15167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String skeleton;
15177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // if both date and time skeleton present,
15187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // the final interval pattern might include time interval patterns
1519bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        // ( when, am_pm, hour, minute, second differ ),
15207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // but not date interval patterns ( when year, month, day differ ).
15217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // For year/month/day differ, it falls back to fall-back pattern.
15227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( timeSkeleton.length() != 0  ) {
15237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            skeleton = timeSkeleton;
15247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
15257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            skeleton = dateSkeleton;
15267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /* interval patterns for skeleton "dMMMy" (but not "dMMMMy")
15297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * are defined in resource,
15307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * interval patterns for skeleton "dMMMMy" are calculated by
15317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * 1. get the best match skeleton for "dMMMMy", which is "dMMMy"
15327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * 2. get the interval patterns for "dMMMy",
15337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * 3. extend "MMM" to "MMMM" in above interval patterns for "dMMMMy"
15347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * getBestSkeleton() is step 1.
15357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
15367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // best skeleton, and the difference information
15377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        BestMatchInfo retValue = fInfo.getBestSkeleton(skeleton);
15387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String bestSkeleton = retValue.bestMatchSkeleton;
15397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int differenceInfo =  retValue.bestMatchDistanceInfo;
15407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1541bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        // Set patterns for fallback use, need to do this
1542bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        // before returning if differenceInfo == -1
1543bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (dateSkeleton.length() != 0  ) {
1544bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            fDatePattern = dtpng.getBestPattern(dateSkeleton);
1545bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
1546bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (timeSkeleton.length() != 0  ) {
1547bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            fTimePattern = dtpng.getBestPattern(timeSkeleton);
1548bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
1549bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
15507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // difference:
15517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // 0 means the best matched skeleton is the same as input skeleton
15527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // 1 means the fields are the same, but field width are different
15537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // 2 means the only difference between fields are v/z,
15547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // -1 means there are other fields difference
1555bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        // (this will happen, for instance, if the supplied skeleton has seconds,
1556bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        //  but no skeletons in the intervalFormats data do)
15577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( differenceInfo == -1 ) {
15587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // skeleton has different fields, not only  v/z difference
15597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return false;
15607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( timeSkeleton.length() == 0 ) {
15637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // only has date skeleton
15647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            genIntervalPattern(Calendar.DATE, skeleton, bestSkeleton, differenceInfo, intervalPatterns);
15657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            SkeletonAndItsBestMatch skeletons = genIntervalPattern(
15667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                  Calendar.MONTH, skeleton,
15677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                  bestSkeleton, differenceInfo,
15687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                  intervalPatterns);
15697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( skeletons != null ) {
15707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                bestSkeleton = skeletons.skeleton;
15717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                skeleton = skeletons.bestMatchSkeleton;
15727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
15737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            genIntervalPattern(Calendar.YEAR, skeleton, bestSkeleton, differenceInfo, intervalPatterns);
15747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
15757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            genIntervalPattern(Calendar.MINUTE, skeleton, bestSkeleton, differenceInfo, intervalPatterns);
15767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            genIntervalPattern(Calendar.HOUR, skeleton, bestSkeleton, differenceInfo, intervalPatterns);
15777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            genIntervalPattern(Calendar.AM_PM, skeleton, bestSkeleton, differenceInfo, intervalPatterns);
15787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
15797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return true;
15807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
15827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
15857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
15867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Generate interval pattern from existing resource
15877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
15887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * It not only save the interval patterns,
15897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * but also return the skeleton and its best match skeleton.
15907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
15917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param field           largest different calendar field
15927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton        skeleton
15937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param bestSkeleton    the best match skeleton which has interval pattern
15947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                        defined in resource
15957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param differenceInfo  the difference between skeleton and best skeleton
15967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *         0 means the best matched skeleton is the same as input skeleton
15977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *         1 means the fields are the same, but field width are different
15987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *         2 means the only difference between fields are v/z,
15997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        -1 means there are other fields difference
16007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
16017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param intervalPatterns interval patterns
16027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
16037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return  an extended skeleton or extended best skeleton if applicable.
16047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *          null otherwise.
16057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
16067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private SkeletonAndItsBestMatch genIntervalPattern(
16077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                   int field, String skeleton, String bestSkeleton,
16087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                   int differenceInfo, Map<String, PatternInfo> intervalPatterns) {
16097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        SkeletonAndItsBestMatch retValue = null;
16107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PatternInfo pattern = fInfo.getIntervalPattern(
16117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                           bestSkeleton, field);
16127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( pattern == null ) {
16137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // single date
16147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( SimpleDateFormat.isFieldUnitIgnored(bestSkeleton, field) ) {
16157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                PatternInfo ptnInfo =
16167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    new PatternInfo(fDateFormat.toPattern(),
16177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                     null,
16187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                     fInfo.getDefaultOrder());
16197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                intervalPatterns.put(DateIntervalInfo.
16207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    CALENDAR_FIELD_TO_PATTERN_LETTER[field], ptnInfo);
16217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return null;
16227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
16237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // for 24 hour system, interval patterns in resource file
16257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // might not include pattern when am_pm differ,
16267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // which should be the same as hour differ.
16277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // add it here for simplicity
16287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( field == Calendar.AM_PM ) {
16297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 pattern = fInfo.getIntervalPattern(bestSkeleton,
16307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                         Calendar.HOUR);
16317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 if ( pattern != null ) {
16327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                      // share
16337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                      intervalPatterns.put(DateIntervalInfo.
16347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                          CALENDAR_FIELD_TO_PATTERN_LETTER[field],
16357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                          pattern);
16367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 }
16377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 return null;
16387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
16397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // else, looking for pattern when 'y' differ for 'dMMMM' skeleton,
16407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // first, get best match pattern "MMMd",
16417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // since there is no pattern for 'y' differs for skeleton 'MMMd',
16427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // need to look for it from skeleton 'yMMMd',
16437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // if found, adjust field width in interval pattern from
16447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // "MMM" to "MMMM".
16457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String fieldLetter =
16467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field];
16477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            bestSkeleton = fieldLetter + bestSkeleton;
16487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            skeleton = fieldLetter + skeleton;
16497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // for example, looking for patterns when 'y' differ for
16507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // skeleton "MMMM".
16517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            pattern = fInfo.getIntervalPattern(bestSkeleton, field);
16527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( pattern == null && differenceInfo == 0 ) {
16537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // if there is no skeleton "yMMMM" defined,
16547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // look for the best match skeleton, for example: "yMMM"
16557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                BestMatchInfo tmpRetValue = fInfo.getBestSkeleton(skeleton);
16567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String tmpBestSkeleton = tmpRetValue.bestMatchSkeleton;
16577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                differenceInfo =  tmpRetValue.bestMatchDistanceInfo;
16587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ( tmpBestSkeleton.length() != 0 && differenceInfo != -1 ) {
16597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    pattern = fInfo.getIntervalPattern(tmpBestSkeleton, field);
16607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    bestSkeleton = tmpBestSkeleton;
16617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
16627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
16637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( pattern != null ) {
16647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                retValue = new SkeletonAndItsBestMatch(skeleton, bestSkeleton);
16657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
16667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( pattern != null ) {
16687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( differenceInfo != 0 ) {
16697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String part1 = adjustFieldWidth(skeleton, bestSkeleton,
16707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                   pattern.getFirstPart(), differenceInfo);
16717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String part2 = adjustFieldWidth(skeleton, bestSkeleton,
16727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                   pattern.getSecondPart(), differenceInfo);
16737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                pattern =  new PatternInfo(part1, part2,
16747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                           pattern.firstDateInPtnIsLaterDate());
16757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
16767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // pattern is immutable, no need to clone;
16777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // pattern = (PatternInfo)pattern.clone();
16787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
16797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            intervalPatterns.put(
16807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field], pattern);
16817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
16827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return retValue;
16837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
16847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
16857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
16867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Adjust field width in best match interval pattern to match
16877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the field width in input skeleton.
16887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
16897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * TODO (xji) make a general solution
16907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The adjusting rule can be:
16917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 1. always adjust
16927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 2. never adjust
16937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 3. default adjust, which means adjust according to the following rules
16947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 3.1 always adjust string, such as MMM and MMMM
16957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 3.2 never adjust between string and numeric, such as MM and MMM
16967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 3.3 always adjust year
16977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 3.4 do not adjust 'd', 'h', or 'm' if h presents
16987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 3.5 do not adjust 'M' if it is numeric(?)
16997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
17007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Since date interval format is well-formed format,
17017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * date and time skeletons are normalized previously,
17027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * till this stage, the adjust here is only "adjust strings, such as MMM
17037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and MMMM, EEE and EEEE.
17047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
17057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param inputSkeleton            the input skeleton
17067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param bestMatchSkeleton        the best match skeleton
17077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param bestMatchIntervalpattern the best match interval pattern
17087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param differenceInfo           the difference between 2 skeletons
17097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                                 1 means only field width differs
17107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                                 2 means v/z exchange
17117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the adjusted interval pattern
17127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
17137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String adjustFieldWidth(String inputSkeleton,
17147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    String bestMatchSkeleton,
17157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    String bestMatchIntervalPattern,
17167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    int differenceInfo ) {
17177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( bestMatchIntervalPattern == null ) {
17197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null; // the 2nd part could be null
17207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
17217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int[] inputSkeletonFieldWidth = new int[58];
17227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int[] bestMatchSkeletonFieldWidth = new int[58];
17237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /* initialize as following
17257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        {
17267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //       A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
17277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            0, 0, 0, 0, 0,  0, 0,  0,  0, 0, 0, 0, 0,  0, 0, 0,
17287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //   P   Q   R   S   T   U   V   W   X   Y   Z
17297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            0, 0, 0, 0, 0,  0, 0,  0,  0, 0, 0, 0, 0,  0, 0, 0,
17307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //       a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
17317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            0, 0, 0, 0, 0,  0, 0,  0,  0, 0, 0, 0, 0,  0, 0, 0,
17327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //   p   q   r   s   t   u   v   w   x   y   z
17337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            0, 0, 0, 0, 0,  0, 0,  0,  0, 0, 0, 0, 0,  0, 0, 0,
17347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        };
17357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        */
17367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DateIntervalInfo.parseSkeleton(inputSkeleton, inputSkeletonFieldWidth);
17397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DateIntervalInfo.parseSkeleton(bestMatchSkeleton, bestMatchSkeletonFieldWidth);
17407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( differenceInfo == 2 ) {
17417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            bestMatchIntervalPattern = bestMatchIntervalPattern.replace('v', 'z');
17427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
17437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder adjustedPtn = new StringBuilder(bestMatchIntervalPattern);
17457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean inQuote = false;
17477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        char prevCh = 0;
17487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int count = 0;
17497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int PATTERN_CHAR_BASE = 0x41;
17517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
17527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // loop through the pattern string character by character
17537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int adjustedPtnLength = adjustedPtn.length();
17547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < adjustedPtnLength; ++i) {
17557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            char ch = adjustedPtn.charAt(i);
17567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (ch != prevCh && count > 0) {
17577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // check the repeativeness of pattern letter
17587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                char skeletonChar = prevCh;
17597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ( skeletonChar == 'L' ) {
17607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // for skeleton "M+", the pattern is "...L..."
17617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    skeletonChar = 'M';
17627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
17637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int fieldCount = bestMatchSkeletonFieldWidth[skeletonChar - PATTERN_CHAR_BASE];
17647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int inputFieldCount = inputSkeletonFieldWidth[skeletonChar - PATTERN_CHAR_BASE];
17657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ( fieldCount == count && inputFieldCount > fieldCount ) {
17667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    count = inputFieldCount - fieldCount;
17677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    for ( int j = 0; j < count; ++j ) {
17687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        adjustedPtn.insert(i, prevCh);
17697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
17707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    i += count;
17717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    adjustedPtnLength += count;
17727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
17737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                count = 0;
17747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
17757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (ch == '\'') {
17767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Consecutive single quotes are a single quote literal,
17777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // either outside of quotes or between quotes
17787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ((i+1) < adjustedPtn.length() && adjustedPtn.charAt(i+1) == '\'') {
17797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ++i;
17807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
17817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    inQuote = ! inQuote;
17827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
17837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
17847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/)
17857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) {
17867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // ch is a date-time pattern character
17877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                prevCh = ch;
17887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++count;
17897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
17907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
17917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( count > 0 ) {
17927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // last item
17937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // check the repeativeness of pattern letter
17947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            char skeletonChar = prevCh;
17957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( skeletonChar == 'L' ) {
17967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // for skeleton "M+", the pattern is "...L..."
17977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                skeletonChar = 'M';
17987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
17997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int fieldCount = bestMatchSkeletonFieldWidth[skeletonChar - PATTERN_CHAR_BASE];
18007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int inputFieldCount = inputSkeletonFieldWidth[skeletonChar - PATTERN_CHAR_BASE];
18017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( fieldCount == count && inputFieldCount > fieldCount ) {
18027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                count = inputFieldCount - fieldCount;
18037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for ( int j = 0; j < count; ++j ) {
18047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    adjustedPtn.append(prevCh);
18057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
18067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
18077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
18087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return adjustedPtn.toString();
18097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
18137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Concat a single date pattern with a time interval pattern,
18147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * set it into the intervalPatterns, while field is time field.
18157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is used to handle time interval patterns on skeleton with
18167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * both time and date. Present the date followed by
18177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the range expression for the time.
18187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param dtfmt                  date and time format
18197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param datePattern            date pattern
18207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param field                  time calendar field: AM_PM, HOUR, MINUTE
18217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param intervalPatterns       interval patterns
18227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
18237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void concatSingleDate2TimeInterval(String dtfmt,
18247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                               String datePattern,
18257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                               int field,
18267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                               Map<String, PatternInfo> intervalPatterns)
18277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
18287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PatternInfo  timeItvPtnInfo =
18307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            intervalPatterns.get(DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field]);
18317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( timeItvPtnInfo != null ) {
18327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String timeIntervalPattern = timeItvPtnInfo.getFirstPart() +
18337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                         timeItvPtnInfo.getSecondPart();
183487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert            String pattern = SimpleFormatterImpl.formatRawPattern(
183587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert                    dtfmt, 2, 2, timeIntervalPattern, datePattern);
18367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            timeItvPtnInfo = DateIntervalInfo.genPatternInfo(pattern,
18377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                timeItvPtnInfo.firstDateInPtnIsLaterDate());
18387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            intervalPatterns.put(
18397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field], timeItvPtnInfo);
18407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
18417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // else: fall back
18427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // it should not happen if the interval format defined is valid
18437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
18477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * check whether a calendar field present in a skeleton.
18487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param field      calendar field need to check
18497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton   given skeleton on which to check the calendar field
18507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return           true if field present in a skeleton.
18517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
18527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static boolean fieldExistsInSkeleton(int field, String skeleton)
18537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
18547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String fieldChar = DateIntervalInfo.CALENDAR_FIELD_TO_PATTERN_LETTER[field];
18557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return ( (skeleton.indexOf(fieldChar) == -1) ? false : true ) ;
18567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
18577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
18597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
18607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * readObject.
18617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
18627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void readObject(ObjectInputStream stream)
18637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        throws IOException, ClassNotFoundException {
18647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        stream.defaultReadObject();
18657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        initializePattern(isDateIntervalInfoDefault ? LOCAL_PATTERN_CACHE : null);
18667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1867f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
1868f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    /**
1869f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * Get the internal patterns for the skeleton
1870f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @internal CLDR
1871f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @deprecated This API is ICU internal only.
1872f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     */
1873f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    @Deprecated
1874f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    public Map<String, PatternInfo> getRawPatterns() {
1875f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        // this is unmodifiable, so ok to return directly
1876f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        return fIntervalPatterns;
1877f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    }
18787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
1879