*
* <P>
* The calendar fields we support for interval formatting are:
- * year, month, date, day-of-week, am-pm, hour, hour-of-day, and minute.
+ * year, month, date, day-of-week, am-pm, hour, hour-of-day, minute, and
+ * second (though we do not currently have specific intervalFormat data for
+ * skeletons with seconds).
* Those calendar fields can be defined in the following order:
- * year > month > date > hour (in day) > minute
+ * year > month > date > hour (in day) > minute > second
*
* The largest different calendar fields between 2 calendars is the
* first different calendar field in above order.
* dtitvinf = new DateIntervalInfo();
*
* // a series of set interval patterns.
- * // Only ERA, YEAR, MONTH, DATE, DAY_OF_MONTH, DAY_OF_WEEK, AM_PM, HOUR, HOUR_OF_DAY, and MINUTE are supported.
+ * // Only ERA, YEAR, MONTH, DATE, DAY_OF_MONTH, DAY_OF_WEEK, AM_PM, HOUR, HOUR_OF_DAY,
+ * MINUTE and SECOND are supported.
* dtitvinf.setIntervalPattern("yMMMd", Calendar.YEAR, "'y ~ y'");
* dtitvinf.setIntervalPattern("yMMMd", Calendar.MONTH, "yyyy 'diff' MMM d - MMM d");
* dtitvinf.setIntervalPattern("yMMMd", Calendar.DATE, "yyyy MMM d ~ d");
/*
* Following are transient interval information
- * relavent (locale) to this formatter.
+ * relevant (locale) to this formatter.
*/
private String fSkeleton = null;
* Interval patterns for this instance's locale.
*/
private transient Map<String, PatternInfo> fIntervalPatterns = null;
+
+ /*
+ * Patterns for fallback formatting.
+ */
+ private String fDatePattern = null;
+ private String fTimePattern = null;
+ private String fDateTimeFormat = null;
/*
other.fInfo = (DateIntervalInfo) fInfo.clone();
other.fFromCalendar = (Calendar) fFromCalendar.clone();
other.fToCalendar = (Calendar) fToCalendar.clone();
+ other.fDatePattern = fDatePattern;
+ other.fTimePattern = fTimePattern;
+ other.fDateTimeFormat = fDateTimeFormat;
return other;
}
* Result is appended to existing contents.
* @param fieldPosition On input: an alignment field, if desired.
* On output: the offsets of the alignment field.
+ * There may be multiple instances of a given field type
+ * in an interval format; in this case the fieldPosition
+ * offsets refer to the first instance.
* @return Reference to 'appendTo' parameter.
* @throws IllegalArgumentException if the formatted object is not
* DateInterval object
* Result is appended to existing contents.
* @param fieldPosition On input: an alignment field, if desired.
* On output: the offsets of the alignment field.
+ * There may be multiple instances of a given field type
+ * in an interval format; in this case the fieldPosition
+ * offsets refer to the first instance.
* @return Reference to 'appendTo' parameter.
* @stable ICU 4.0
*/
} else if ( fromCalendar.get(Calendar.MINUTE) !=
toCalendar.get(Calendar.MINUTE) ) {
field = Calendar.MINUTE;
+ } else if ( fromCalendar.get(Calendar.SECOND) !=
+ toCalendar.get(Calendar.SECOND) ) {
+ field = Calendar.SECOND;
} else {
return null;
}
* Result is appended to existing contents.
* @param pos On input: an alignment field, if desired.
* On output: the offsets of the alignment field.
+ * There may be multiple instances of a given field type
+ * in an interval format; in this case the fieldPosition
+ * offsets refer to the first instance.
* @return Reference to 'appendTo' parameter.
* @throws IllegalArgumentException if the two calendars are not equivalent.
* @stable ICU 4.0
} else if ( fromCalendar.get(Calendar.MINUTE) !=
toCalendar.get(Calendar.MINUTE) ) {
field = Calendar.MINUTE;
- } else {
- /* ignore the second/millisecond etc. small fields' difference.
+ } else if ( fromCalendar.get(Calendar.SECOND) !=
+ toCalendar.get(Calendar.SECOND) ) {
+ field = Calendar.SECOND;
+ } else {
+ /* ignore the millisecond etc. small fields' difference.
* use single date when all the above are the same.
*/
return fDateFormat.format(fromCalendar, appendTo, pos);
}
+ boolean fromToOnSameDay = (field==Calendar.AM_PM || field==Calendar.HOUR || field==Calendar.MINUTE || field==Calendar.SECOND);
// get interval pattern
PatternInfo intervalPattern = fIntervalPatterns.get(
return fDateFormat.format(fromCalendar, appendTo, pos);
}
- return fallbackFormat(fromCalendar, toCalendar, appendTo, pos);
+ return fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, pos);
}
// If the first part in interval pattern is empty,
// For a 'real' interval pattern, the first part will never be empty.
if ( intervalPattern.getFirstPart() == null ) {
// fall back
- return fallbackFormat(fromCalendar, toCalendar, appendTo, pos,
+ return fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, pos,
intervalPattern.getSecondPart());
}
Calendar firstCal;
fDateFormat.format(firstCal, appendTo, pos);
if ( intervalPattern.getSecondPart() != null ) {
fDateFormat.applyPattern(intervalPattern.getSecondPart());
- fDateFormat.format(secondCal, appendTo, pos);
+ FieldPosition otherPos = new FieldPosition(pos.getField());
+ fDateFormat.format(secondCal, appendTo, otherPos);
+ if (pos.getEndIndex() == 0 && otherPos.getEndIndex() > 0) {
+ pos = otherPos;
+ }
}
fDateFormat.applyPattern(originalPattern);
return appendTo;
}
+ private void adjustPosition(String combiningPattern, // has {0} and {1} in it
+ String pat0, FieldPosition pos0, // pattern and pos corresponding to {0}
+ String pat1, FieldPosition pos1, // pattern and pos corresponding to {1}
+ FieldPosition posResult) {
+ int index0 = combiningPattern.indexOf("{0}");
+ int index1 = combiningPattern.indexOf("{1}");
+ if (index0 < 0 || index1 < 0) {
+ return;
+ }
+ int placeholderLen = 3; // length of "{0}" or "{1}"
+ if (index0 < index1) {
+ if (pos0.getEndIndex() > 0) {
+ posResult.setBeginIndex(pos0.getBeginIndex() + index0);
+ posResult.setEndIndex(pos0.getEndIndex() + index0);
+ } else if (pos1.getEndIndex() > 0) {
+ // here index1 >= 3
+ index1 += pat0.length() - placeholderLen; // adjust for pat0 replacing {0}
+ posResult.setBeginIndex(pos1.getBeginIndex() + index1);
+ posResult.setEndIndex(pos1.getEndIndex() + index1);
+ }
+ } else {
+ if (pos1.getEndIndex() > 0) {
+ posResult.setBeginIndex(pos1.getBeginIndex() + index1);
+ posResult.setEndIndex(pos1.getEndIndex() + index1);
+ } else if (pos0.getEndIndex() > 0) {
+ // here index0 >= 3
+ index0 += pat1.length() - placeholderLen; // adjust for pat1 replacing {1}
+ posResult.setBeginIndex(pos0.getBeginIndex() + index0);
+ posResult.setEndIndex(pos0.getEndIndex() + index0);
+ }
+ }
+ }
/*
* Format 2 Calendars to using fall-back interval pattern
*/
private final StringBuffer fallbackFormat(Calendar fromCalendar,
Calendar toCalendar,
+ boolean fromToOnSameDay,
StringBuffer appendTo,
FieldPosition pos) {
+ String fullPattern = null; // for saving the pattern in fDateFormat
+ boolean formatDatePlusTimeRange = (fromToOnSameDay && fDatePattern != null && fTimePattern != null);
// the fall back
+ if (formatDatePlusTimeRange) {
+ fullPattern = fDateFormat.toPattern(); // save current pattern, restore later
+ fDateFormat.applyPattern(fTimePattern);
+ }
+ FieldPosition otherPos = new FieldPosition(pos.getField());
StringBuffer earlierDate = new StringBuffer(64);
earlierDate = fDateFormat.format(fromCalendar, earlierDate, pos);
StringBuffer laterDate = new StringBuffer(64);
- laterDate = fDateFormat.format(toCalendar, laterDate, pos);
+ laterDate = fDateFormat.format(toCalendar, laterDate, otherPos);
String fallbackPattern = fInfo.getFallbackIntervalPattern();
- String fallback = MessageFormat.format(fallbackPattern, new Object[]
+ adjustPosition(fallbackPattern, earlierDate.toString(), pos, laterDate.toString(), otherPos, pos);
+ String fallbackRange = MessageFormat.format(fallbackPattern, new Object[]
{earlierDate.toString(), laterDate.toString()});
- appendTo.append(fallback);
+ if (formatDatePlusTimeRange) {
+ // fallbackRange has just the time range, need to format the date part and combine that
+ fDateFormat.applyPattern(fDatePattern);
+ StringBuffer datePortion = new StringBuffer(64);
+ otherPos.setBeginIndex(0);
+ otherPos.setEndIndex(0);
+ datePortion = fDateFormat.format(fromCalendar, datePortion, otherPos);
+ adjustPosition(fDateTimeFormat, fallbackRange, pos, datePortion.toString(), otherPos, pos);
+ fallbackRange = MessageFormat.format(fDateTimeFormat, new Object[]
+ {fallbackRange, datePortion.toString()});
+ }
+ appendTo.append(fallbackRange);
+ if (formatDatePlusTimeRange) {
+ // restore full pattern
+ fDateFormat.applyPattern(fullPattern);
+ }
return appendTo;
}
*/
private final StringBuffer fallbackFormat(Calendar fromCalendar,
Calendar toCalendar,
+ boolean fromToOnSameDay,
StringBuffer appendTo,
FieldPosition pos,
String fullPattern) {
String originalPattern = fDateFormat.toPattern();
fDateFormat.applyPattern(fullPattern);
- fallbackFormat(fromCalendar, toCalendar, appendTo, pos);
+ fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, pos);
fDateFormat.applyPattern(originalPattern);
return appendTo;
}
String normalizedDateSkeleton = normalizedDate.toString();
String normalizedTimeSkeleton = normalizedTime.toString();
+ // move this up here since we need it for fallbacks
+ if (time.length() != 0 && date.length() != 0) {
+ // Need the Date/Time pattern for concatnation the date with
+ // the time interval.
+ // The date/time pattern ( such as {0} {1} ) is saved in
+ // calendar, that is why need to get the CalendarData here.
+ CalendarData calData = new CalendarData(locale, null);
+ String[] patterns = calData.getDateTimePatterns();
+ fDateTimeFormat = patterns[8];
+ }
+
boolean found = genSeparateDateTimePtn(normalizedDateSkeleton,
normalizedTimeSkeleton,
- intervalPatterns);
+ intervalPatterns, dtpng);
+ // for skeletons with seconds, found is false and we enter this block
if ( found == false ) {
// use fallback
// TODO: if user asks "m", but "d" differ
* 2) otherwise, present the date followed by the
* range expression for the time.
*/
- // Need the Date/Time pattern for concatnation the date with
- // the time interval.
- // The date/time pattern ( such as {0} {1} ) is saved in
- // calendar, that is why need to get the CalendarData here.
- CalendarData calData = new CalendarData(locale, null);
- String[] patterns = calData.getDateTimePatterns();
+ if (fDateTimeFormat == null) {
+ fDateTimeFormat = "{1} {0}";
+ }
String datePattern =dtpng.getBestPattern(dateSkeleton);
- concatSingleDate2TimeInterval(patterns[8], datePattern, Calendar.AM_PM, intervalPatterns);
- concatSingleDate2TimeInterval(patterns[8], datePattern, Calendar.HOUR, intervalPatterns);
- concatSingleDate2TimeInterval(patterns[8], datePattern, Calendar.MINUTE, intervalPatterns);
+ concatSingleDate2TimeInterval(fDateTimeFormat, datePattern, Calendar.AM_PM, intervalPatterns);
+ concatSingleDate2TimeInterval(fDateTimeFormat, datePattern, Calendar.HOUR, intervalPatterns);
+ concatSingleDate2TimeInterval(fDateTimeFormat, datePattern, Calendar.MINUTE, intervalPatterns);
}
return intervalPatterns;
*/
private boolean genSeparateDateTimePtn(String dateSkeleton,
String timeSkeleton,
- Map<String, PatternInfo> intervalPatterns)
+ Map<String, PatternInfo> intervalPatterns,
+ DateTimePatternGenerator dtpng)
{
String skeleton;
// if both date and time skeleton present,
// the final interval pattern might include time interval patterns
- // ( when, am_pm, hour, minute differ ),
+ // ( when, am_pm, hour, minute, second differ ),
// but not date interval patterns ( when year, month, day differ ).
// For year/month/day differ, it falls back to fall-back pattern.
if ( timeSkeleton.length() != 0 ) {
String bestSkeleton = retValue.bestMatchSkeleton;
int differenceInfo = retValue.bestMatchDistanceInfo;
+ // Set patterns for fallback use, need to do this
+ // before returning if differenceInfo == -1
+ if (dateSkeleton.length() != 0 ) {
+ fDatePattern = dtpng.getBestPattern(dateSkeleton);
+ }
+ if (timeSkeleton.length() != 0 ) {
+ fTimePattern = dtpng.getBestPattern(timeSkeleton);
+ }
+
// difference:
// 0 means the best matched skeleton is the same as input skeleton
// 1 means the fields are the same, but field width are different
// 2 means the only difference between fields are v/z,
// -1 means there are other fields difference
+ // (this will happen, for instance, if the supplied skeleton has seconds,
+ // but no skeletons in the intervalFormats data do)
if ( differenceInfo == -1 ) {
// skeleton has different fields, not only v/z difference
return false;
*
* <P>
* The calendar fields we support for interval formatting are:
- * year, month, date, day-of-week, am-pm, hour, hour-of-day, and minute.
+ * year, month, date, day-of-week, am-pm, hour, hour-of-day, minute, and
+ * second (though we do not currently have specific intervalFormat data for
+ * skeletons with seconds).
* Those calendar fields can be defined in the following order:
- * year > month > date > am-pm > hour > minute
+ * year > month > date > am-pm > hour > minute > second
*
* The largest different calendar fields between 2 calendars is the
* first different calendar field in above order.
* the interval patterns using setIntervalPattern function as so desired.
* Currently, users can only set interval patterns when the following
* calendar fields are different: ERA, YEAR, MONTH, DATE, DAY_OF_MONTH,
- * DAY_OF_WEEK, AM_PM, HOUR, HOUR_OF_DAY, and MINUTE.
+ * DAY_OF_WEEK, AM_PM, HOUR, HOUR_OF_DAY, MINUTE and SECOND.
* Interval patterns when other calendar fields are different is not supported.
* <P>
* DateIntervalInfo objects are cloneable.
private static final long serialVersionUID = 1;
private static final int MINIMUM_SUPPORTED_CALENDAR_FIELD =
- Calendar.MINUTE;
+ Calendar.SECOND;
//private static boolean DEBUG = true;
private static String FALLBACK_STRING = "fallback";
key = CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.HOUR];
} else if ( key.equals(CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.MINUTE]) ) {
calendarField = Calendar.MINUTE;
+ } else if ( key.equals(CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.SECOND]) ) {
+ calendarField = Calendar.SECOND;
}
if ( calendarField != -1 ) {
* Restriction:
* Currently, users can only set interval patterns when the following
* calendar fields are different: ERA, YEAR, MONTH, DATE, DAY_OF_MONTH,
- * DAY_OF_WEEK, AM_PM, HOUR, HOUR_OF_DAY, and MINUTE.
+ * DAY_OF_WEEK, AM_PM, HOUR, HOUR_OF_DAY, MINUTE, and SECOND.
* Interval patterns when other calendar fields are different are
* not supported.
*
public PatternInfo getIntervalPattern(String skeleton, int field)
{
if ( field > MINIMUM_SUPPORTED_CALENDAR_FIELD ) {
- throw new IllegalArgumentException("no support for field less than MINUTE");
+ throw new IllegalArgumentException("no support for field less than SECOND");
}
Map<String, PatternInfo> patternsOfOneSkeleton = fIntervalPatterns.get(skeleton);
if ( patternsOfOneSkeleton != null ) {
"en", "2007 01 10 10:10:10", "2007 01 10 10:10:20", "hhmmzz", "10:10 AM PST",
- "en", "2007 01 10 10:10:10", "2007 01 10 10:10:20", "hms", "10:10:10 AM",
+ "en", "2007 01 10 10:10:10", "2007 01 10 10:10:20", "hms", "10:10:10 AM \u2013 10:10:20 AM",
"en", "2007 01 01 22:00:00", "2007 01 01 23:00:00", "yMMMMdHm", "January 1, 2007, 22:00 \u2013 23:00",
*/
public void TestGetIntervalPattern(){
// Tests when "if ( field > MINIMUM_SUPPORTED_CALENDAR_FIELD )" is true
- // MINIMUM_SUPPORTED_CALENDAR_FIELD = Calendar.MINUTE;
+ // MINIMUM_SUPPORTED_CALENDAR_FIELD = Calendar.SECOND;
DateIntervalInfo dii = new DateIntervalInfo();
try{
- dii.getIntervalPattern("", Calendar.MINUTE+1);
+ dii.getIntervalPattern("", Calendar.SECOND+1);
errln("DateIntervalInfo.getIntervalPattern(String,int) was suppose " +
"to return an exception for the 'int field' parameter " +
"when it exceeds MINIMUM_SUPPORTED_CALENDAR_FIELD.");
} catch(Exception e){}
// Tests when "if ( lrgDiffCalUnit > MINIMUM_SUPPORTED_CALENDAR_FIELD )" is true
- // MINIMUM_SUPPORTED_CALENDAR_FIELD = Calendar.MINUTE;
+ // MINIMUM_SUPPORTED_CALENDAR_FIELD = Calendar.SECOND;
try{
dii = (DateIntervalInfo) dii.cloneAsThawed();
- dii.setIntervalPattern("", Calendar.MINUTE+1, "");
+ dii.setIntervalPattern("", Calendar.SECOND+1, "");
errln("DateIntervalInfo.setIntervalPattern(String,int,String) " +
"was suppose to return an exception when the " +
"variable 'lrgDiffCalUnit' is greater than " +
}
return false;
}
+
+ public void TestFPos_SkelWithSeconds () {
+
+ final long[] deltas = {
+ 0L, // none
+ 200L, // 200 millisec
+ 20000L, // 20 sec
+ 1200000L, // 20 min
+ 7200000L, // 2 hrs
+ 43200000L, // 12 hrs
+ 691200000L, // 8 days
+ 1382400000L, // 16 days,
+ 8640000000L, // 100 days
+ };
+
+ class ExpectPosAndFormat {
+ public int posBegin;
+ public int posEnd;
+ public String format;
+ // Simple constructor
+ public ExpectPosAndFormat(int pBegin, int pEnd, String fmt) {
+ posBegin = pBegin;
+ posEnd = pEnd;
+ format = fmt;
+ }
+ };
+
+ final ExpectPosAndFormat[] exp_en_HHmm = {
+ new ExpectPosAndFormat( 3, 5, "09:00" ),
+ new ExpectPosAndFormat( 3, 5, "09:00" ),
+ new ExpectPosAndFormat( 3, 5, "09:00" ),
+ new ExpectPosAndFormat( 3, 5, "09:00 \u2013 09:20" ),
+ new ExpectPosAndFormat( 3, 5, "09:00 \u2013 11:00" ),
+ new ExpectPosAndFormat( 3, 5, "09:00 \u2013 21:00" ),
+ new ExpectPosAndFormat( 15, 17, "11/20/2014, 09:00 \u2013 11/28/2014, 09:00" ),
+ new ExpectPosAndFormat( 15, 17, "11/20/2014, 09:00 \u2013 12/6/2014, 09:00" ),
+ new ExpectPosAndFormat( 15, 17, "11/20/2014, 09:00 \u2013 2/28/2015, 09:00" )
+ };
+
+ final ExpectPosAndFormat[] exp_en_HHmmss = {
+ new ExpectPosAndFormat( 3, 5, "09:00:00" ),
+ new ExpectPosAndFormat( 3, 5, "09:00:00" ),
+ new ExpectPosAndFormat( 3, 5, "09:00:00 \u2013 09:00:20" ),
+ new ExpectPosAndFormat( 3, 5, "09:00:00 \u2013 09:20:00" ),
+ new ExpectPosAndFormat( 3, 5, "09:00:00 \u2013 11:00:00" ),
+ new ExpectPosAndFormat( 3, 5, "09:00:00 \u2013 21:00:00" ),
+ new ExpectPosAndFormat( 15, 17, "11/20/2014, 09:00:00 \u2013 11/28/2014, 09:00:00" ),
+ new ExpectPosAndFormat( 15, 17, "11/20/2014, 09:00:00 \u2013 12/6/2014, 09:00:00" ),
+ new ExpectPosAndFormat( 15, 17, "11/20/2014, 09:00:00 \u2013 2/28/2015, 09:00:00" )
+ };
+
+ final ExpectPosAndFormat[] exp_en_yyMMdd = {
+ new ExpectPosAndFormat( 0, 0, "11/20/14" ),
+ new ExpectPosAndFormat( 0, 0, "11/20/14" ),
+ new ExpectPosAndFormat( 0, 0, "11/20/14" ),
+ new ExpectPosAndFormat( 0, 0, "11/20/14" ),
+ new ExpectPosAndFormat( 0, 0, "11/20/14" ),
+ new ExpectPosAndFormat( 0, 0, "11/20/14" ),
+ new ExpectPosAndFormat( 0, 0, "11/20/14 \u2013 11/28/14" ),
+ new ExpectPosAndFormat( 0, 0, "11/20/14 \u2013 12/6/14" ),
+ new ExpectPosAndFormat( 0, 0, "11/20/14 \u2013 2/28/15" )
+ };
+
+ final ExpectPosAndFormat[] exp_en_yyMMddHHmm = {
+ new ExpectPosAndFormat( 13, 15, "11/20/14, 09:00" ),
+ new ExpectPosAndFormat( 13, 15, "11/20/14, 09:00" ),
+ new ExpectPosAndFormat( 13, 15, "11/20/14, 09:00" ),
+ new ExpectPosAndFormat( 13, 15, "11/20/14, 09:00 \u2013 09:20" ),
+ new ExpectPosAndFormat( 13, 15, "11/20/14, 09:00 \u2013 11:00" ),
+ new ExpectPosAndFormat( 13, 15, "11/20/14, 09:00 \u2013 21:00" ),
+ new ExpectPosAndFormat( 13, 15, "11/20/14, 09:00 \u2013 11/28/14, 09:00" ),
+ new ExpectPosAndFormat( 13, 15, "11/20/14, 09:00 \u2013 12/06/14, 09:00" ),
+ new ExpectPosAndFormat( 13, 15, "11/20/14, 09:00 \u2013 02/28/15, 09:00" )
+ };
+
+ final ExpectPosAndFormat[] exp_en_yyMMddHHmmss = {
+ new ExpectPosAndFormat( 13, 15, "11/20/14, 09:00:00" ),
+ new ExpectPosAndFormat( 13, 15, "11/20/14, 09:00:00" ),
+ new ExpectPosAndFormat( 13, 15, "11/20/14, 09:00:00 \u2013 09:00:20" ),
+ new ExpectPosAndFormat( 13, 15, "11/20/14, 09:00:00 \u2013 09:20:00" ),
+ new ExpectPosAndFormat( 13, 15, "11/20/14, 09:00:00 \u2013 11:00:00" ),
+ new ExpectPosAndFormat( 13, 15, "11/20/14, 09:00:00 \u2013 21:00:00" ),
+ new ExpectPosAndFormat( 13, 15, "11/20/14, 09:00:00 \u2013 11/28/14, 09:00:00" ),
+ new ExpectPosAndFormat( 13, 15, "11/20/14, 09:00:00 \u2013 12/06/14, 09:00:00" ),
+ new ExpectPosAndFormat( 13, 15, "11/20/14, 09:00:00 \u2013 02/28/15, 09:00:00" )
+ };
+
+ final ExpectPosAndFormat[] exp_en_yMMMdhmmssz = {
+ new ExpectPosAndFormat( 16, 18, "Nov 20, 2014, 9:00:00 AM GMT" ),
+ new ExpectPosAndFormat( 16, 18, "Nov 20, 2014, 9:00:00 AM GMT" ),
+ new ExpectPosAndFormat( 16, 18, "Nov 20, 2014, 9:00:00 AM GMT \u2013 9:00:20 AM GMT" ),
+ new ExpectPosAndFormat( 16, 18, "Nov 20, 2014, 9:00:00 AM GMT \u2013 9:20:00 AM GMT" ),
+ new ExpectPosAndFormat( 16, 18, "Nov 20, 2014, 9:00:00 AM GMT \u2013 11:00:00 AM GMT" ),
+ new ExpectPosAndFormat( 16, 18, "Nov 20, 2014, 9:00:00 AM GMT \u2013 9:00:00 PM GMT" ),
+ new ExpectPosAndFormat( 16, 18, "Nov 20, 2014, 9:00:00 AM GMT \u2013 Nov 28, 2014, 9:00:00 AM GMT" ),
+ new ExpectPosAndFormat( 16, 18, "Nov 20, 2014, 9:00:00 AM GMT \u2013 Dec 6, 2014, 9:00:00 AM GMT" ),
+ new ExpectPosAndFormat( 16, 18, "Nov 20, 2014, 9:00:00 AM GMT \u2013 Feb 28, 2015, 9:00:00 AM GMT" )
+ };
+
+ final ExpectPosAndFormat[] exp_ja_yyMMddHHmm = {
+ new ExpectPosAndFormat( 11, 13, "14/11/20 9:00" ),
+ new ExpectPosAndFormat( 11, 13, "14/11/20 9:00" ),
+ new ExpectPosAndFormat( 11, 13, "14/11/20 9:00" ),
+ new ExpectPosAndFormat( 11, 13, "14/11/20 9\u664200\u5206\uFF5E9\u664220\u5206" ),
+ new ExpectPosAndFormat( 11, 13, "14/11/20 9\u664200\u5206\uFF5E11\u664200\u5206" ),
+ new ExpectPosAndFormat( 11, 13, "14/11/20 9\u664200\u5206\uFF5E21\u664200\u5206" ),
+ new ExpectPosAndFormat( 11, 13, "14/11/20 9:00\uFF5E14/11/28 9:00" ),
+ new ExpectPosAndFormat( 11, 13, "14/11/20 9:00\uFF5E14/12/06 9:00" ),
+ new ExpectPosAndFormat( 11, 13, "14/11/20 9:00\uFF5E15/02/28 9:00" )
+ };
+
+ final ExpectPosAndFormat[] exp_ja_yyMMddHHmmss = {
+ new ExpectPosAndFormat( 11, 13, "14/11/20 9:00:00" ),
+ new ExpectPosAndFormat( 11, 13, "14/11/20 9:00:00" ),
+ new ExpectPosAndFormat( 11, 13, "14/11/20 9:00:00\uFF5E9:00:20" ),
+ new ExpectPosAndFormat( 11, 13, "14/11/20 9:00:00\uFF5E9:20:00" ),
+ new ExpectPosAndFormat( 11, 13, "14/11/20 9:00:00\uFF5E11:00:00" ),
+ new ExpectPosAndFormat( 11, 13, "14/11/20 9:00:00\uFF5E21:00:00" ),
+ new ExpectPosAndFormat( 11, 13, "14/11/20 9:00:00\uFF5E14/11/28 9:00:00" ),
+ new ExpectPosAndFormat( 11, 13, "14/11/20 9:00:00\uFF5E14/12/06 9:00:00" ),
+ new ExpectPosAndFormat( 11, 13, "14/11/20 9:00:00\uFF5E15/02/28 9:00:00" )
+ };
+
+ final ExpectPosAndFormat[] exp_ja_yMMMdHHmmss = {
+ new ExpectPosAndFormat( 14, 16, "2014\u5E7411\u670820\u65E5 9:00:00" ),
+ new ExpectPosAndFormat( 14, 16, "2014\u5E7411\u670820\u65E5 9:00:00" ),
+ new ExpectPosAndFormat( 14, 16, "2014\u5E7411\u670820\u65E5 9:00:00\uFF5E9:00:20" ),
+ new ExpectPosAndFormat( 14, 16, "2014\u5E7411\u670820\u65E5 9:00:00\uFF5E9:20:00" ),
+ new ExpectPosAndFormat( 14, 16, "2014\u5E7411\u670820\u65E5 9:00:00\uFF5E11:00:00" ),
+ new ExpectPosAndFormat( 14, 16, "2014\u5E7411\u670820\u65E5 9:00:00\uFF5E21:00:00" ),
+ new ExpectPosAndFormat( 14, 16, "2014\u5E7411\u670820\u65E5 9:00:00\uFF5E2014\u5E7411\u670828\u65E5 9:00:00" ),
+ new ExpectPosAndFormat( 14, 16, "2014\u5E7411\u670820\u65E5 9:00:00\uFF5E2014\u5E7412\u67086\u65E5 9:00:00" ),
+ new ExpectPosAndFormat( 14, 16, "2014\u5E7411\u670820\u65E5 9:00:00\uFF5E2015\u5E742\u670828\u65E5 9:00:00" )
+ };
+
+ class LocaleAndSkeletonItem {
+ public String locale;
+ public String skeleton;
+ public int fieldToCheck;
+ public ExpectPosAndFormat[] expected;
+ // Simple constructor
+ public LocaleAndSkeletonItem(String loc, String skel, int field, ExpectPosAndFormat[] exp) {
+ locale = loc;
+ skeleton = skel;
+ fieldToCheck = field;
+ expected = exp;
+ }
+ };
+
+ final LocaleAndSkeletonItem[] locSkelItems = {
+ new LocaleAndSkeletonItem( "en", "HHmm", DateFormat.MINUTE_FIELD, exp_en_HHmm ),
+ new LocaleAndSkeletonItem( "en", "HHmmss", DateFormat.MINUTE_FIELD, exp_en_HHmmss ),
+ new LocaleAndSkeletonItem( "en", "yyMMdd", DateFormat.MINUTE_FIELD, exp_en_yyMMdd ),
+ new LocaleAndSkeletonItem( "en", "yyMMddHHmm", DateFormat.MINUTE_FIELD, exp_en_yyMMddHHmm ),
+ new LocaleAndSkeletonItem( "en", "yyMMddHHmmss", DateFormat.MINUTE_FIELD, exp_en_yyMMddHHmmss ),
+ // skip the following until ICU4J DateIntervalFormat has support for setting time zone
+ // new LocaleAndSkeletonItem( "en", "yMMMdhmmssz", DateFormat.MINUTE_FIELD, exp_en_yMMMdhmmssz ),
+ new LocaleAndSkeletonItem( "ja", "yyMMddHHmm", DateFormat.MINUTE_FIELD, exp_ja_yyMMddHHmm ),
+ new LocaleAndSkeletonItem( "ja", "yyMMddHHmmss", DateFormat.MINUTE_FIELD, exp_ja_yyMMddHHmmss ),
+ new LocaleAndSkeletonItem( "ja", "yMMMdHHmmss", DateFormat.MINUTE_FIELD, exp_ja_yMMMdHHmmss )
+ };
+
+ //final String zoneGMT = "GMT";
+ final long startTimeGMT = 1416474000000L; // 2014 Nov 20 09:00 GMT
+
+ TimeZone localZone = TimeZone.getDefault();
+ long startTime = startTimeGMT - localZone.getOffset(startTimeGMT);
+ for (LocaleAndSkeletonItem item: locSkelItems) {
+ DateIntervalFormat difmt = DateIntervalFormat.getInstance(item.skeleton, new ULocale(item.locale));
+ int dIdx, dCount = deltas.length;
+ for (dIdx = 0; dIdx < dCount; dIdx++) {
+ DateInterval di = new DateInterval(startTime, startTime + deltas[dIdx]);
+ StringBuffer actual = new StringBuffer(64);
+ FieldPosition pos = new FieldPosition(item.fieldToCheck);
+ String actualString = difmt.format(di, actual, pos).toString();
+ ExpectPosAndFormat expectPosFmt = item.expected[dIdx];
+ if (!actualString.equals(expectPosFmt.format) ||
+ pos.getBeginIndex() != expectPosFmt.posBegin || pos.getEndIndex() != expectPosFmt.posEnd) {
+ errln("For locale " + item.locale + ", skeleton " + item.skeleton + ", delta " + deltas[dIdx] +
+ ": expect " + expectPosFmt.posBegin + "-" + expectPosFmt.posEnd + " \"" + expectPosFmt.format +
+ "\"; get " + pos.getBeginIndex() + "-" + pos.getEndIndex() + " \"" + actualString + "\"");
+ }
+ }
+ }
+ }
}