ICU-20967 add millisecond to DateIntervalFormat

See #978
This commit is contained in:
Frank Tang 2020-02-22 01:41:58 +00:00 committed by Frank Yung-Fong Tang
parent e572de5516
commit be3ee4cc63
9 changed files with 202 additions and 23 deletions

View file

@ -444,6 +444,9 @@ DateIntervalFormat::formatImpl(Calendar& fromCalendar,
} else if ( fromCalendar.get(UCAL_SECOND, status) !=
toCalendar.get(UCAL_SECOND, status) ) {
field = UCAL_SECOND;
} else if ( fromCalendar.get(UCAL_MILLISECOND, status) !=
toCalendar.get(UCAL_MILLISECOND, status) ) {
field = UCAL_MILLISECOND;
}
if ( U_FAILURE(status) ) {
@ -455,7 +458,7 @@ DateIntervalFormat::formatImpl(Calendar& fromCalendar,
*/
return fDateFormat->_format(fromCalendar, appendTo, fphandler, status);
}
UBool fromToOnSameDay = (field==UCAL_AM_PM || field==UCAL_HOUR || field==UCAL_MINUTE || field==UCAL_SECOND);
UBool fromToOnSameDay = (field==UCAL_AM_PM || field==UCAL_HOUR || field==UCAL_MINUTE || field==UCAL_SECOND || field==UCAL_MILLISECOND);
// following call should not set wrong status,
// all the pass-in fields are valid till here

View file

@ -711,6 +711,9 @@ DateIntervalInfo::calendarFieldToIntervalIndex(UCalendarDateFields field,
case UCAL_SECOND:
index = kIPI_SECOND;
break;
case UCAL_MILLISECOND:
index = kIPI_MILLISECOND;
break;
default:
status = U_ILLEGAL_ARGUMENT_ERROR;
}

View file

@ -174,11 +174,12 @@ class U_I18N_API FormattedDateInterval : public UMemory, public FormattedValue {
*
* <P>
* The calendar fields we support for interval formatting are:
* year, month, date, day-of-week, am-pm, hour, hour-of-day, minute, and second
* year, month, date, day-of-week, am-pm, hour, hour-of-day, minute, second,
* and millisecond.
* (though we do not currently have specific intervalFormat date for skeletons
* with seconds).
* with seconds and millisecond).
* Those calendar fields can be defined in the following order:
* year > month > date > hour (in day) > minute > second
* year > month > date > hour (in day) > minute > second > millisecond
*
* The largest different calendar fields between 2 calendars is the
* first different calendar field in above order.

View file

@ -137,8 +137,8 @@ U_NAMESPACE_BEGIN
* After a DateIntervalInfo object is created, clients may modify
* 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.
* calendar fields are different: ERA, YEAR, MONTH, DATE, DAY_OF_MONTH,
* DAY_OF_WEEK, AM_PM, HOUR, HOUR_OF_DAY, MINUTE, SECOND, and MILLISECOND.
* Interval patterns when other calendar fields are different is not supported.
* <P>
* DateIntervalInfo objects are cloneable.
@ -245,7 +245,7 @@ public:
* 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, SECOND and MILLISECOND.
* Interval patterns when other calendar fields are different are
* not supported.
*
@ -348,7 +348,7 @@ private:
/**
* Following is for saving the interval patterns.
* We only support interval patterns on
* ERA, YEAR, MONTH, DAY, AM_PM, HOUR, and MINUTE
* ERA, YEAR, MONTH, DAY, AM_PM, HOUR, MINUTE, SECOND and MILLISECOND.
*/
enum IntervalPatternIndex
{
@ -360,6 +360,7 @@ private:
kIPI_HOUR,
kIPI_MINUTE,
kIPI_SECOND,
kIPI_MILLISECOND,
kIPI_MAX_INDEX
};
public:
@ -453,8 +454,8 @@ private:
* hash table.
*
* Since we only support the following calendar fields:
* ERA, YEAR, MONTH, DATE, DAY_OF_MONTH, DAY_OF_WEEK,
* AM_PM, HOUR, HOUR_OF_DAY, and MINUTE,
* ERA, YEAR, MONTH, DATE, DAY_OF_MONTH, DAY_OF_WEEK,
* AM_PM, HOUR, HOUR_OF_DAY, MINUTE, SECOND, and MILLISECOND.
* We reserve only 4 interval patterns for a skeleton.
*
* @param field calendar field

View file

@ -59,6 +59,7 @@ void DateIntervalFormatTest::runIndexedTest( int32_t index, UBool exec, const ch
TESTCASE(10, testFormattedDateInterval);
TESTCASE(11, testCreateInstanceForAllLocales);
TESTCASE(12, testTicket20707);
TESTCASE(13, testFormatMillisecond);
default: name = ""; break;
}
}
@ -1806,6 +1807,115 @@ void DateIntervalFormatTest::testCreateInstanceForAllLocales() {
}
}
void DateIntervalFormatTest::testFormatMillisecond() {
struct
{
int year;
int month;
int day;
int from_hour;
int from_miniute;
int from_second;
int from_millisecond;
int to_hour;
int to_miniute;
int to_second;
int to_millisecond;
const char* skeleton;
const char16_t* expected;
}
kTestCases [] =
{
// From To
// y m d h m s ms h m s ms skeleton expected
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 45, 321, "ms", u"23:45"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 45, 321, "msS", u"23:45.3"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 45, 321, "msSS", u"23:45.32"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 45, 321, "msSSS", u"23:45.321"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 45, 987, "ms", u"23:45"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 45, 987, "msS", u"23:45.3 \u2013 23:45.9"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 45, 987, "msSS", u"23:45.32 \u2013 23:45.98"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 45, 987, "msSSS", u"23:45.321 \u2013 23:45.987"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 46, 987, "ms", u"23:45 \u2013 23:46"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 46, 987, "msS", u"23:45.3 \u2013 23:46.9"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 46, 987, "msSS", u"23:45.32 \u2013 23:46.98"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 46, 987, "msSSS", u"23:45.321 \u2013 23:46.987"},
{ 2019, 2, 10, 1, 23, 45, 321, 2, 24, 45, 987, "ms", u"23:45 \u2013 24:45"},
{ 2019, 2, 10, 1, 23, 45, 321, 2, 24, 45, 987, "msS", u"23:45.3 \u2013 24:45.9"},
{ 2019, 2, 10, 1, 23, 45, 321, 2, 24, 45, 987, "msSS", u"23:45.32 \u2013 24:45.98"},
{ 2019, 2, 10, 1, 23, 45, 321, 2, 24, 45, 987, "msSSS", u"23:45.321 \u2013 24:45.987"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 45, 321, "s", u"45"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 45, 321, "sS", u"45.3"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 45, 321, "sSS", u"45.32"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 45, 321, "sSSS", u"45.321"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 45, 987, "s", u"45"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 45, 987, "sS", u"45.3 \u2013 45.9"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 45, 987, "sSS", u"45.32 \u2013 45.98"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 45, 987, "sSSS", u"45.321 \u2013 45.987"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 46, 987, "s", u"45 \u2013 46"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 46, 987, "sS", u"45.3 \u2013 46.9"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 46, 987, "sSS", u"45.32 \u2013 46.98"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 46, 987, "sSSS", u"45.321 \u2013 46.987"},
{ 2019, 2, 10, 1, 23, 45, 321, 2, 24, 45, 987, "s", u"45 \u2013 45"},
{ 2019, 2, 10, 1, 23, 45, 321, 2, 24, 45, 987, "sS", u"45.3 \u2013 45.9"},
{ 2019, 2, 10, 1, 23, 45, 321, 2, 24, 45, 987, "sSS", u"45.32 \u2013 45.98"},
{ 2019, 2, 10, 1, 23, 45, 321, 2, 24, 45, 987, "sSSS", u"45.321 \u2013 45.987"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 45, 321, "S", u"3"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 45, 321, "SS", u"32"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 45, 321, "SSS", u"321"},
// Same millisecond but in different second.
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 46, 321, "S", u"3 \u2013 3"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 46, 321, "SS", u"32 \u2013 32"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 46, 321, "SSS", u"321 \u2013 321"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 45, 987, "S", u"3 \u2013 9"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 45, 987, "SS", u"32 \u2013 98"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 45, 987, "SSS", u"321 \u2013 987"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 46, 987, "S", u"3 \u2013 9"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 46, 987, "SS", u"32 \u2013 98"},
{ 2019, 2, 10, 1, 23, 45, 321, 1, 23, 46, 987, "SSS", u"321 \u2013 987"},
{ 2019, 2, 10, 1, 23, 45, 321, 2, 24, 45, 987, "S", u"3 \u2013 9"},
{ 2019, 2, 10, 1, 23, 45, 321, 2, 24, 45, 987, "SS", u"32 \u2013 98"},
{ 2019, 2, 10, 1, 23, 45, 321, 2, 24, 45, 987, "SSS", u"321 \u2013 987"},
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, nullptr, nullptr},
};
const Locale &enLocale = Locale::getEnglish();
IcuTestErrorCode status(*this, "testFormatMillisecond");
LocalPointer<Calendar> calendar(Calendar::createInstance(enLocale, status));
if (status.errIfFailureAndReset()) { return; }
for (int32_t i = 0; kTestCases[i].year > 0; i++) {
LocalPointer<DateIntervalFormat> fmt(DateIntervalFormat::createInstance(
kTestCases[i].skeleton, enLocale, status));
if (status.errIfFailureAndReset()) { continue; }
calendar->clear();
calendar->set(kTestCases[i].year, kTestCases[i].month, kTestCases[i].day,
kTestCases[i].from_hour, kTestCases[i].from_miniute, kTestCases[i].from_second);
UDate from = calendar->getTime(status) + kTestCases[i].from_millisecond;
if (status.errIfFailureAndReset()) { continue; }
calendar->clear();
calendar->set(kTestCases[i].year, kTestCases[i].month, kTestCases[i].day,
kTestCases[i].to_hour, kTestCases[i].to_miniute, kTestCases[i].to_second);
UDate to = calendar->getTime(status) + kTestCases[i].to_millisecond;
FormattedDateInterval res = fmt->formatToValue(DateInterval(from, to), status);
if (status.errIfFailureAndReset()) { continue; }
UnicodeString formatted = res.toString(status);
if (status.errIfFailureAndReset()) { continue; }
if (formatted != kTestCases[i].expected) {
std::string tmp1;
std::string tmp2;
errln("Case %d for skeleton %s : Got %s but expect %s",
i, kTestCases[i].skeleton, formatted.toUTF8String<std::string>(tmp1).c_str(),
UnicodeString(kTestCases[i].expected).toUTF8String<std::string>(tmp2).c_str());
}
}
}
void DateIntervalFormatTest::testTicket20707() {
IcuTestErrorCode status(*this, "testTicket20707");

View file

@ -34,6 +34,8 @@ public:
*/
void testFormat();
void testFormatMillisecond();
/**
* Test formatting using user defined DateIntervalInfo
*/

View file

@ -230,7 +230,7 @@ import com.ibm.icu.util.UResourceBundle;
*
* // a series of set interval patterns.
* // Only ERA, YEAR, MONTH, DATE, DAY_OF_MONTH, DAY_OF_WEEK, AM_PM, HOUR, HOUR_OF_DAY,
* MINUTE and SECOND are supported.
* MINUTE, SECOND and MILLISECOND 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");
@ -856,6 +856,9 @@ public class DateIntervalFormat extends UFormat {
} else if ( fromCalendar.get(Calendar.SECOND) !=
toCalendar.get(Calendar.SECOND) ) {
field = Calendar.SECOND;
} else if ( fromCalendar.get(Calendar.MILLISECOND) !=
toCalendar.get(Calendar.MILLISECOND) ) {
field = Calendar.MILLISECOND;
} else {
return null;
}
@ -952,16 +955,19 @@ public class DateIntervalFormat extends UFormat {
} else if ( fromCalendar.get(Calendar.MINUTE) !=
toCalendar.get(Calendar.MINUTE) ) {
field = Calendar.MINUTE;
} else if ( fromCalendar.get(Calendar.SECOND) !=
} else if ( fromCalendar.get(Calendar.SECOND) !=
toCalendar.get(Calendar.SECOND) ) {
field = Calendar.SECOND;
} else {
} else if ( fromCalendar.get(Calendar.MILLISECOND) !=
toCalendar.get(Calendar.MILLISECOND) ) {
field = Calendar.MILLISECOND;
} else {
/* ignore the millisecond etc. small fields' difference.
* use single date when all the above are the same.
*/
return fDateFormat.format(fromCalendar, appendTo, pos, attributes);
}
boolean fromToOnSameDay = (field==Calendar.AM_PM || field==Calendar.HOUR || field==Calendar.MINUTE || field==Calendar.SECOND);
boolean fromToOnSameDay = (field==Calendar.AM_PM || field==Calendar.HOUR || field==Calendar.MINUTE || field==Calendar.SECOND || field==Calendar.MILLISECOND);
// get interval pattern
PatternInfo intervalPattern = fIntervalPatterns.get(

View file

@ -143,7 +143,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, MINUTE and SECOND.
* DAY_OF_WEEK, AM_PM, HOUR, HOUR_OF_DAY, MINUTE, SECOND, and MILLISECOND.
* Interval patterns when other calendar fields are different is not supported.
* <P>
* DateIntervalInfo objects are cloneable.
@ -298,7 +298,7 @@ public class DateIntervalInfo implements Cloneable, Freezable<DateIntervalInfo>,
private static final long serialVersionUID = 1;
private static final int MINIMUM_SUPPORTED_CALENDAR_FIELD =
Calendar.SECOND;
Calendar.MILLISECOND;
//private static boolean DEBUG = true;
private static String CALENDAR_KEY = "calendar";
@ -425,8 +425,9 @@ public class DateIntervalInfo implements Cloneable, Freezable<DateIntervalInfo>,
* Calendar.HOUR_OF_DAY
* Calendar.MINUTE
* Calendar.SECOND
* Calendar.MILLISECOND
*/
private static final String ACCEPTED_PATTERN_LETTERS = "GyMdahHms";
private static final String ACCEPTED_PATTERN_LETTERS = "GyMdahHmsS";
// Output data
DateIntervalInfo dateIntervalInfo;
@ -705,7 +706,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, MINUTE, and SECOND.
* DAY_OF_WEEK, AM_PM, HOUR, HOUR_OF_DAY, MINUTE, SECOND, and MILLISECOND.
* Interval patterns when other calendar fields are different are
* not supported.
*
@ -850,7 +851,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 SECOND");
throw new IllegalArgumentException("no support for field less than MILLISECOND");
}
Map<String, PatternInfo> patternsOfOneSkeleton = fIntervalPatterns.get(skeleton);
if ( patternsOfOneSkeleton != null ) {

View file

@ -1253,10 +1253,10 @@ public class DateIntervalFormatTest extends TestFmwk {
@Test
public void TestGetIntervalPattern(){
// Tests when "if ( field > MINIMUM_SUPPORTED_CALENDAR_FIELD )" is true
// MINIMUM_SUPPORTED_CALENDAR_FIELD = Calendar.SECOND;
// MINIMUM_SUPPORTED_CALENDAR_FIELD = Calendar.MILLISECOND;
DateIntervalInfo dii = new DateIntervalInfo();
try{
dii.getIntervalPattern("", Calendar.SECOND+1);
dii.getIntervalPattern("", Calendar.MILLISECOND+1);
errln("DateIntervalInfo.getIntervalPattern(String,int) was suppose " +
"to return an exception for the 'int field' parameter " +
"when it exceeds MINIMUM_SUPPORTED_CALENDAR_FIELD.");
@ -1279,10 +1279,10 @@ public class DateIntervalFormatTest extends TestFmwk {
} catch(Exception e){}
// Tests when "if ( lrgDiffCalUnit > MINIMUM_SUPPORTED_CALENDAR_FIELD )" is true
// MINIMUM_SUPPORTED_CALENDAR_FIELD = Calendar.SECOND;
// MINIMUM_SUPPORTED_CALENDAR_FIELD = Calendar.MILLISECOND;
try{
dii = dii.cloneAsThawed();
dii.setIntervalPattern("", Calendar.SECOND+1, "");
dii.setIntervalPattern("", Calendar.MILLISECOND+1, "");
errln("DateIntervalInfo.setIntervalPattern(String,int,String) " +
"was suppose to return an exception when the " +
"variable 'lrgDiffCalUnit' is greater than " +
@ -2094,4 +2094,56 @@ public class DateIntervalFormatTest extends TestFmwk {
i++;
}
}
@Test
public void testFormatMillisecond() {
Object[][] kTestCases = {
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 45), 321, "ms", "23:45"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 45), 321, "msS", "23:45.3"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 45), 321, "msSS", "23:45.32"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 45), 321, "msSSS", "23:45.321"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 45), 987, "ms", "23:45"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 45), 987, "msS", "23:45.3 23:45.9"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 45), 987, "msSS", "23:45.32 23:45.98"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 45), 987, "msSSS", "23:45.321 23:45.987"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 46), 987, "ms", "23:45 23:46"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 46), 987, "msS", "23:45.3 23:46.9"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 46), 987, "msSS", "23:45.32 23:46.98"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 46), 987, "msSSS", "23:45.321 23:46.987"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 2, 24, 45), 987, "ms", "23:45 24:45"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 2, 24, 45), 987, "msS", "23:45.3 24:45.9"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 2, 24, 45), 987, "msSS", "23:45.32 24:45.98"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 2, 24, 45), 987, "msSSS", "23:45.321 24:45.987"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 45), 321, "s", "45"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 45), 321, "sS", "45.3"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 45), 321, "sSS", "45.32"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 45), 321, "sSSS", "45.321"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 45), 987, "s", "45"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 45), 987, "sS", "45.3 45.9"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 45), 987, "sSS", "45.32 45.98"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 45), 987, "sSSS", "45.321 45.987"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 46), 987, "s", "45 46"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 46), 987, "sS", "45.3 46.9"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 46), 987, "sSS", "45.32 46.98"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 1, 23, 46), 987, "sSSS", "45.321 46.987"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 2, 24, 45), 987, "s", "45 45"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 2, 24, 45), 987, "sS", "45.3 45.9"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 2, 24, 45), 987, "sSS", "45.32 45.98"},
{ new Date(2019, 2, 10, 1, 23, 45), 321, new Date(2019, 2, 10, 2, 24, 45), 987, "sSSS", "45.321 45.987"},
};
Locale enLocale = Locale.ENGLISH;
for (Object[] testCase : kTestCases) {
DateIntervalFormat fmt = DateIntervalFormat.getInstance((String)testCase[4], enLocale);
Date fromDate = (Date)testCase[0];
long from = fromDate.getTime() + (Integer)testCase[1];
Date toDate = (Date)testCase[2];
long to = toDate.getTime() + (Integer)testCase[3];
FormattedDateInterval res = fmt.formatToValue(new DateInterval(from, to));
assertEquals("Formate for " + testCase[4], testCase[5], res.toString());
}
}
}