ICU-11706 (and ) Fix DateIntervalFormat handling of (1) skeletons with seconds, (2) FieldPosition (J)

X-SVN-Rev: 37615
This commit is contained in:
Peter Edberg 2015-06-24 07:40:11 +00:00
parent a8325d335c
commit a1b8a08bf7
3 changed files with 336 additions and 36 deletions
icu4j/main
classes/core/src/com/ibm/icu/text
tests/core/src/com/ibm/icu/dev/test/format

View file

@ -85,9 +85,11 @@ import com.ibm.icu.util.ULocale.Category;
*
* <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.
@ -216,7 +218,8 @@ import com.ibm.icu.util.ULocale.Category;
* 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");
@ -307,7 +310,7 @@ public class DateIntervalFormat extends UFormat {
/*
* Following are transient interval information
* relavent (locale) to this formatter.
* relevant (locale) to this formatter.
*/
private String fSkeleton = null;
@ -321,6 +324,13 @@ public class DateIntervalFormat extends UFormat {
* 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;
/*
@ -556,6 +566,9 @@ public class DateIntervalFormat extends UFormat {
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;
}
@ -572,6 +585,9 @@ public class DateIntervalFormat extends UFormat {
* 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
@ -596,6 +612,9 @@ public class DateIntervalFormat extends UFormat {
* 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
*/
@ -638,6 +657,9 @@ public class DateIntervalFormat extends UFormat {
} 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;
}
@ -657,6 +679,9 @@ public class DateIntervalFormat extends UFormat {
* 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
@ -694,12 +719,16 @@ public class DateIntervalFormat extends UFormat {
} 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(
@ -714,7 +743,7 @@ public class DateIntervalFormat extends UFormat {
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,
@ -722,7 +751,7 @@ public class DateIntervalFormat extends UFormat {
// 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;
@ -741,12 +770,48 @@ public class DateIntervalFormat extends UFormat {
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
@ -766,17 +831,41 @@ public class DateIntervalFormat extends UFormat {
*/
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;
}
@ -800,12 +889,13 @@ public class DateIntervalFormat extends UFormat {
*/
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;
}
@ -1030,10 +1120,22 @@ public class DateIntervalFormat extends UFormat {
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
@ -1137,16 +1239,13 @@ public class DateIntervalFormat extends UFormat {
* 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;
@ -1383,12 +1482,13 @@ public class DateIntervalFormat extends UFormat {
*/
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 ) {
@ -1410,11 +1510,22 @@ public class DateIntervalFormat extends UFormat {
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;

View file

@ -71,9 +71,11 @@ import com.ibm.icu.util.UResourceBundle;
*
* <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.
@ -134,7 +136,7 @@ import com.ibm.icu.util.UResourceBundle;
* 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.
@ -285,7 +287,7 @@ public class DateIntervalInfo implements Cloneable, Freezable<DateIntervalInfo>,
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";
@ -478,6 +480,8 @@ public class DateIntervalInfo implements Cloneable, Freezable<DateIntervalInfo>,
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 ) {
@ -592,7 +596,7 @@ public class DateIntervalInfo implements Cloneable, Freezable<DateIntervalInfo>,
* 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.
*
@ -737,7 +741,7 @@ public class DateIntervalInfo implements Cloneable, Freezable<DateIntervalInfo>,
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 ) {

View file

@ -410,7 +410,7 @@ public class DateIntervalFormatTest extends com.ibm.icu.dev.test.TestFmwk {
"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",
@ -1196,10 +1196,10 @@ public class DateIntervalFormatTest extends com.ibm.icu.dev.test.TestFmwk {
*/
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.");
@ -1221,10 +1221,10 @@ public class DateIntervalFormatTest extends com.ibm.icu.dev.test.TestFmwk {
} 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 " +
@ -1528,4 +1528,189 @@ public class DateIntervalFormatTest extends com.ibm.icu.dev.test.TestFmwk {
}
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 + "\"");
}
}
}
}
}