mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-21 12:40:02 +00:00
ICU-11706 (and #11726) Fix DateIntervalFormat handling of (1) skeletons with seconds, (2) FieldPosition
X-SVN-Rev: 37613
This commit is contained in:
parent
15ee90b9a3
commit
a8325d335c
5 changed files with 404 additions and 56 deletions
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (C) 2008-2014, International Business Machines Corporation and
|
||||
* Copyright (C) 2008-2015, International Business Machines Corporation and
|
||||
* others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
*
|
||||
|
@ -117,7 +117,10 @@ DateIntervalFormat::DateIntervalFormat()
|
|||
fDateFormat(NULL),
|
||||
fFromCalendar(NULL),
|
||||
fToCalendar(NULL),
|
||||
fDtpng(NULL)
|
||||
fDtpng(NULL),
|
||||
fDatePattern(NULL),
|
||||
fTimePattern(NULL),
|
||||
fDateTimeFormat(NULL)
|
||||
{}
|
||||
|
||||
|
||||
|
@ -127,7 +130,10 @@ DateIntervalFormat::DateIntervalFormat(const DateIntervalFormat& itvfmt)
|
|||
fDateFormat(NULL),
|
||||
fFromCalendar(NULL),
|
||||
fToCalendar(NULL),
|
||||
fDtpng(NULL) {
|
||||
fDtpng(NULL),
|
||||
fDatePattern(NULL),
|
||||
fTimePattern(NULL),
|
||||
fDateTimeFormat(NULL) {
|
||||
*this = itvfmt;
|
||||
}
|
||||
|
||||
|
@ -140,6 +146,9 @@ DateIntervalFormat::operator=(const DateIntervalFormat& itvfmt) {
|
|||
delete fFromCalendar;
|
||||
delete fToCalendar;
|
||||
delete fDtpng;
|
||||
delete fDatePattern;
|
||||
delete fTimePattern;
|
||||
delete fDateTimeFormat;
|
||||
if ( itvfmt.fDateFormat ) {
|
||||
fDateFormat = (SimpleDateFormat*)itvfmt.fDateFormat->clone();
|
||||
} else {
|
||||
|
@ -167,7 +176,12 @@ DateIntervalFormat::operator=(const DateIntervalFormat& itvfmt) {
|
|||
}
|
||||
if (itvfmt.fDtpng) {
|
||||
fDtpng = itvfmt.fDtpng->clone();
|
||||
} else {
|
||||
fDtpng = NULL;
|
||||
}
|
||||
fDatePattern = (itvfmt.fDatePattern)? (UnicodeString*)itvfmt.fDatePattern->clone(): NULL;
|
||||
fTimePattern = (itvfmt.fTimePattern)? (UnicodeString*)itvfmt.fTimePattern->clone(): NULL;
|
||||
fDateTimeFormat = (itvfmt.fDateTimeFormat)? (UnicodeString*)itvfmt.fDateTimeFormat->clone(): NULL;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
@ -179,6 +193,9 @@ DateIntervalFormat::~DateIntervalFormat() {
|
|||
delete fFromCalendar;
|
||||
delete fToCalendar;
|
||||
delete fDtpng;
|
||||
delete fDatePattern;
|
||||
delete fTimePattern;
|
||||
delete fDateTimeFormat;
|
||||
}
|
||||
|
||||
|
||||
|
@ -201,6 +218,9 @@ DateIntervalFormat::operator==(const Format& other) const {
|
|||
equal = fFromCalendar->isEquivalentTo(*fmt->fFromCalendar) ;
|
||||
equal = fToCalendar->isEquivalentTo(*fmt->fToCalendar) ;
|
||||
equal = (fSkeleton == fmt->fSkeleton);
|
||||
equal = ((fDatePattern == NULL && fmt->fDatePattern == NULL) || (fDatePattern && fmt->fDatePattern && *fDatePattern == *fmt->fDatePattern));
|
||||
equal = ((fTimePattern == NULL && fmt->fTimePattern == NULL) || (fTimePattern && fmt->fTimePattern && *fTimePattern == *fmt->fTimePattern));
|
||||
equal = ((fDateTimeFormat == NULL && fmt->fDateTimeFormat == NULL) || (fDateTimeFormat && fmt->fDateTimeFormat && *fDateTimeFormat == *fmt->fDateTimeFormat));
|
||||
#endif
|
||||
UBool res;
|
||||
res = ( this == fmt ) ||
|
||||
|
@ -214,6 +234,9 @@ DateIntervalFormat::operator==(const Format& other) const {
|
|||
fToCalendar &&
|
||||
fToCalendar->isEquivalentTo(*fmt->fToCalendar) &&
|
||||
fSkeleton == fmt->fSkeleton &&
|
||||
((fDatePattern == NULL && fmt->fDatePattern == NULL) || (fDatePattern && fmt->fDatePattern && *fDatePattern == *fmt->fDatePattern)) &&
|
||||
((fTimePattern == NULL && fmt->fTimePattern == NULL) || (fTimePattern && fmt->fTimePattern && *fTimePattern == *fmt->fTimePattern)) &&
|
||||
((fDateTimeFormat == NULL && fmt->fDateTimeFormat == NULL) || (fDateTimeFormat && fmt->fDateTimeFormat && *fDateTimeFormat == *fmt->fDateTimeFormat)) &&
|
||||
fDtpng &&
|
||||
(*fDtpng == *fmt->fDtpng) );
|
||||
int8_t i;
|
||||
|
@ -314,17 +337,21 @@ DateIntervalFormat::format(Calendar& fromCalendar,
|
|||
} else if ( fromCalendar.get(UCAL_MINUTE, status) !=
|
||||
toCalendar.get(UCAL_MINUTE, status) ) {
|
||||
field = UCAL_MINUTE;
|
||||
} else if ( fromCalendar.get(UCAL_SECOND, status) !=
|
||||
toCalendar.get(UCAL_SECOND, status) ) {
|
||||
field = UCAL_SECOND;
|
||||
}
|
||||
|
||||
if ( U_FAILURE(status) ) {
|
||||
return appendTo;
|
||||
}
|
||||
if ( field == UCAL_FIELD_COUNT ) {
|
||||
/* ignore the second/millisecond etc. small fields' difference.
|
||||
/* ignore the millisecond etc. small fields' difference.
|
||||
* use single date when all the above are the same.
|
||||
*/
|
||||
return fDateFormat->format(fromCalendar, appendTo, pos);
|
||||
}
|
||||
UBool fromToOnSameDay = (field==UCAL_AM_PM || field==UCAL_HOUR || field==UCAL_MINUTE || field==UCAL_SECOND);
|
||||
|
||||
// following call should not set wrong status,
|
||||
// all the pass-in fields are valid till here
|
||||
|
@ -341,7 +368,7 @@ DateIntervalFormat::format(Calendar& fromCalendar,
|
|||
*/
|
||||
return fDateFormat->format(fromCalendar, appendTo, pos);
|
||||
}
|
||||
return fallbackFormat(fromCalendar, toCalendar, appendTo, pos, status);
|
||||
return fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, pos, status);
|
||||
}
|
||||
// If the first part in interval pattern is empty,
|
||||
// the 2nd part of it saves the full-pattern used in fall-back.
|
||||
|
@ -351,7 +378,7 @@ DateIntervalFormat::format(Calendar& fromCalendar,
|
|||
UnicodeString originalPattern;
|
||||
fDateFormat->toPattern(originalPattern);
|
||||
fDateFormat->applyPattern(intervalPattern.secondPart);
|
||||
appendTo = fallbackFormat(fromCalendar, toCalendar, appendTo, pos, status);
|
||||
appendTo = fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, pos, status);
|
||||
fDateFormat->applyPattern(originalPattern);
|
||||
return appendTo;
|
||||
}
|
||||
|
@ -372,7 +399,12 @@ DateIntervalFormat::format(Calendar& fromCalendar,
|
|||
fDateFormat->format(*firstCal, appendTo, pos);
|
||||
if ( !intervalPattern.secondPart.isEmpty() ) {
|
||||
fDateFormat->applyPattern(intervalPattern.secondPart);
|
||||
fDateFormat->format(*secondCal, appendTo, pos);
|
||||
FieldPosition otherPos;
|
||||
otherPos.setField(pos.getField());
|
||||
fDateFormat->format(*secondCal, appendTo, otherPos);
|
||||
if (pos.getEndIndex() == 0 && otherPos.getEndIndex() > 0) {
|
||||
pos = otherPos;
|
||||
}
|
||||
}
|
||||
fDateFormat->applyPattern(originalPattern);
|
||||
return appendTo;
|
||||
|
@ -468,7 +500,10 @@ DateIntervalFormat::DateIntervalFormat(const Locale& locale,
|
|||
fDateFormat(NULL),
|
||||
fFromCalendar(NULL),
|
||||
fToCalendar(NULL),
|
||||
fDtpng(NULL)
|
||||
fDtpng(NULL),
|
||||
fDatePattern(NULL),
|
||||
fTimePattern(NULL),
|
||||
fDateTimeFormat(NULL)
|
||||
{
|
||||
if ( U_FAILURE(status) ) {
|
||||
delete dtItvInfo;
|
||||
|
@ -636,10 +671,39 @@ DateIntervalFormat::initializePattern(UErrorCode& status) {
|
|||
PRINTMESG(mesg)
|
||||
#endif
|
||||
|
||||
// move this up here since we need it for fallbacks
|
||||
if ( timeSkeleton.length() > 0 && dateSkeleton.length() > 0 ) {
|
||||
// Need the Date/Time pattern for concatenation of 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, status);
|
||||
if ( U_FAILURE(status) ) {
|
||||
delete calData;
|
||||
return;
|
||||
}
|
||||
if ( calData == NULL ) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
const UResourceBundle* dateTimePatternsRes = calData->getByKey(
|
||||
gDateTimePatternsTag, status);
|
||||
int32_t dateTimeFormatLength;
|
||||
const UChar* dateTimeFormat = ures_getStringByIndex(
|
||||
dateTimePatternsRes,
|
||||
(int32_t)DateFormat::kDateTime,
|
||||
&dateTimeFormatLength, &status);
|
||||
if ( U_SUCCESS(status) && dateTimeFormatLength >= 3 ) {
|
||||
fDateTimeFormat = new UnicodeString(dateTimeFormat, dateTimeFormatLength);
|
||||
}
|
||||
delete calData;
|
||||
}
|
||||
|
||||
UBool found = setSeparateDateTimePtn(normalizedDateSkeleton,
|
||||
normalizedTimeSkeleton);
|
||||
|
||||
// for skeletons with seconds, found is false and we enter this block
|
||||
if ( found == false ) {
|
||||
// use fallback
|
||||
// TODO: if user asks "m"(minute), but "d"(day) differ
|
||||
|
@ -716,42 +780,17 @@ DateIntervalFormat::initializePattern(UErrorCode& status) {
|
|||
* 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, status);
|
||||
|
||||
if ( U_FAILURE(status) ) {
|
||||
delete calData;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( calData == NULL ) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
const UResourceBundle* dateTimePatternsRes = calData->getByKey(
|
||||
gDateTimePatternsTag, status);
|
||||
int32_t dateTimeFormatLength;
|
||||
const UChar* dateTimeFormat = ures_getStringByIndex(
|
||||
dateTimePatternsRes,
|
||||
(int32_t)DateFormat::kDateTime,
|
||||
&dateTimeFormatLength, &status);
|
||||
if ( U_FAILURE(status) ) {
|
||||
if ( fDateTimeFormat == 0 ) {
|
||||
// earlier failure getting dateTimeFormat
|
||||
return;
|
||||
}
|
||||
|
||||
UnicodeString datePattern = fDtpng->getBestPattern(dateSkeleton, status);
|
||||
|
||||
concatSingleDate2TimeInterval(dateTimeFormat, dateTimeFormatLength,
|
||||
datePattern, UCAL_AM_PM, status);
|
||||
concatSingleDate2TimeInterval(dateTimeFormat, dateTimeFormatLength,
|
||||
datePattern, UCAL_HOUR, status);
|
||||
concatSingleDate2TimeInterval(dateTimeFormat, dateTimeFormatLength,
|
||||
datePattern, UCAL_MINUTE, status);
|
||||
delete calData;
|
||||
concatSingleDate2TimeInterval(*fDateTimeFormat, datePattern, UCAL_AM_PM, status);
|
||||
concatSingleDate2TimeInterval(*fDateTimeFormat, datePattern, UCAL_HOUR, status);
|
||||
concatSingleDate2TimeInterval(*fDateTimeFormat, datePattern, UCAL_MINUTE, status);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -966,11 +1005,25 @@ DateIntervalFormat::setSeparateDateTimePtn(
|
|||
return false;
|
||||
}
|
||||
|
||||
// Set patterns for fallback use, need to do this
|
||||
// before returning if differenceInfo == -1
|
||||
UErrorCode status;
|
||||
if ( dateSkeleton.length() != 0 && fDtpng != NULL ) {
|
||||
status = U_ZERO_ERROR;
|
||||
fDatePattern = new UnicodeString(fDtpng->getBestPattern(dateSkeleton, status));
|
||||
}
|
||||
if ( timeSkeleton.length() != 0 && fDtpng != NULL ) {
|
||||
status = U_ZERO_ERROR;
|
||||
fTimePattern = new UnicodeString(fDtpng->getBestPattern(timeSkeleton, status));
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
@ -1264,33 +1317,98 @@ DateIntervalFormat::splitPatternInto2Part(const UnicodeString& intervalPattern)
|
|||
return (i - count);
|
||||
}
|
||||
|
||||
static const UChar bracketedZero[] = {0x7B,0x30,0x7D};
|
||||
static const UChar bracketedOne[] = {0x7B,0x31,0x7D};
|
||||
|
||||
void
|
||||
DateIntervalFormat::adjustPosition(UnicodeString& combiningPattern, // has {0} and {1} in it
|
||||
UnicodeString& pat0, FieldPosition& pos0, // pattern and pos corresponding to {0}
|
||||
UnicodeString& pat1, FieldPosition& pos1, // pattern and pos corresponding to {1}
|
||||
FieldPosition& posResult) {
|
||||
int32_t index0 = combiningPattern.indexOf(bracketedZero, 3, 0);
|
||||
int32_t index1 = combiningPattern.indexOf(bracketedOne, 3, 0);
|
||||
if (index0 < 0 || index1 < 0) {
|
||||
return;
|
||||
}
|
||||
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() - 3; // 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() - 3; // adjust for pat1 replacing {1}
|
||||
posResult.setBeginIndex(pos0.getBeginIndex() + index0);
|
||||
posResult.setEndIndex(pos0.getEndIndex() + index0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UnicodeString&
|
||||
DateIntervalFormat::fallbackFormat(Calendar& fromCalendar,
|
||||
Calendar& toCalendar,
|
||||
UBool fromToOnSameDay, // new
|
||||
UnicodeString& appendTo,
|
||||
FieldPosition& pos,
|
||||
UErrorCode& status) const {
|
||||
if ( U_FAILURE(status) ) {
|
||||
return appendTo;
|
||||
}
|
||||
UnicodeString fullPattern; // for saving the pattern in fDateFormat
|
||||
UBool formatDatePlusTimeRange = (fromToOnSameDay && fDatePattern && fTimePattern);
|
||||
// the fall back
|
||||
// no need delete earlierDate and laterDate since they are adopted
|
||||
if (formatDatePlusTimeRange) {
|
||||
fDateFormat->toPattern(fullPattern); // save current pattern, restore later
|
||||
fDateFormat->applyPattern(*fTimePattern);
|
||||
}
|
||||
FieldPosition otherPos;
|
||||
otherPos.setField(pos.getField());
|
||||
UnicodeString* earlierDate = new UnicodeString();
|
||||
*earlierDate = fDateFormat->format(fromCalendar, *earlierDate, pos);
|
||||
fDateFormat->format(fromCalendar, *earlierDate, pos);
|
||||
UnicodeString* laterDate = new UnicodeString();
|
||||
*laterDate = fDateFormat->format(toCalendar, *laterDate, pos);
|
||||
fDateFormat->format(toCalendar, *laterDate, otherPos);
|
||||
UnicodeString fallbackPattern;
|
||||
fInfo->getFallbackIntervalPattern(fallbackPattern);
|
||||
adjustPosition(fallbackPattern, *earlierDate, pos, *laterDate, otherPos, pos);
|
||||
Formattable fmtArray[2];
|
||||
fmtArray[0].adoptString(earlierDate);
|
||||
fmtArray[1].adoptString(laterDate);
|
||||
|
||||
UnicodeString fallback;
|
||||
MessageFormat::format(fallbackPattern, fmtArray, 2, fallback, status);
|
||||
UnicodeString fallbackRange;
|
||||
MessageFormat::format(fallbackPattern, fmtArray, 2, fallbackRange, status);
|
||||
if ( U_SUCCESS(status) ) {
|
||||
appendTo.append(fallback);
|
||||
if (!formatDatePlusTimeRange) {
|
||||
appendTo.append(fallbackRange);
|
||||
} else {
|
||||
// fallbackRange has just the time range, need to format the date part and combine that
|
||||
fDateFormat->applyPattern(*fDatePattern);
|
||||
UnicodeString* datePortion = new UnicodeString();
|
||||
otherPos.setBeginIndex(0);
|
||||
otherPos.setEndIndex(0);
|
||||
fDateFormat->format(fromCalendar, *datePortion, otherPos);
|
||||
adjustPosition(*fDateTimeFormat, fallbackRange, pos, *datePortion, otherPos, pos);
|
||||
fmtArray[0].setString(fallbackRange); // {0} is time range
|
||||
fmtArray[1].adoptString(datePortion); // {1} is single date portion
|
||||
fallbackRange.remove();
|
||||
MessageFormat::format(*fDateTimeFormat, fmtArray, 2, fallbackRange, status);
|
||||
if ( U_SUCCESS(status) ) {
|
||||
appendTo.append(fallbackRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (formatDatePlusTimeRange) {
|
||||
// restore full pattern
|
||||
fDateFormat->applyPattern(fullPattern);
|
||||
}
|
||||
return appendTo;
|
||||
}
|
||||
|
@ -1420,8 +1538,7 @@ DateIntervalFormat::adjustFieldWidth(const UnicodeString& inputSkeleton,
|
|||
|
||||
|
||||
void
|
||||
DateIntervalFormat::concatSingleDate2TimeInterval(const UChar* format,
|
||||
int32_t formatLen,
|
||||
DateIntervalFormat::concatSingleDate2TimeInterval(UnicodeString& format,
|
||||
const UnicodeString& datePattern,
|
||||
UCalendarDateFields field,
|
||||
UErrorCode& status) {
|
||||
|
@ -1441,8 +1558,7 @@ DateIntervalFormat::concatSingleDate2TimeInterval(const UChar* format,
|
|||
fmtArray[0].adoptString(timeIntervalPattern);
|
||||
fmtArray[1].adoptString(dateStr);
|
||||
UnicodeString combinedPattern;
|
||||
MessageFormat::format(UnicodeString(TRUE, format, formatLen),
|
||||
fmtArray, 2, combinedPattern, status);
|
||||
MessageFormat::format(format, fmtArray, 2, combinedPattern, status);
|
||||
if ( U_FAILURE(status) ) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -574,6 +574,9 @@ DateIntervalInfo::calendarFieldToIntervalIndex(UCalendarDateFields field,
|
|||
case UCAL_MINUTE:
|
||||
index = kIPI_MINUTE;
|
||||
break;
|
||||
case UCAL_SECOND:
|
||||
index = kIPI_SECOND;
|
||||
break;
|
||||
default:
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/********************************************************************************
|
||||
* Copyright (C) 2008-2013, International Business Machines Corporation and
|
||||
* Copyright (C) 2008-2013,2015, International Business Machines Corporation and
|
||||
* others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
*
|
||||
|
@ -93,9 +93,11 @@ U_NAMESPACE_BEGIN
|
|||
*
|
||||
* <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 date 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.
|
||||
|
@ -664,6 +666,21 @@ private:
|
|||
* Below are for generating interval patterns local to the formatter
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param combiningPattern xxx
|
||||
* @param pat0 xxx
|
||||
* @param pos0 xxx
|
||||
* @param pat1 xxx
|
||||
* @param pos1 xxx
|
||||
* @param posResult xxx
|
||||
*/
|
||||
static void
|
||||
adjustPosition(UnicodeString& combiningPattern, // has {0} and {1} in it
|
||||
UnicodeString& pat0, FieldPosition& pos0, // pattern and pos corresponding to {0}
|
||||
UnicodeString& pat1, FieldPosition& pos1, // pattern and pos corresponding to {1}
|
||||
FieldPosition& posResult);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Format 2 Calendars using fall-back interval pattern
|
||||
|
@ -675,6 +692,8 @@ private:
|
|||
* to be formatted into date interval string
|
||||
* @param toCalendar calendar set to the to date in date interval
|
||||
* to be formatted into date interval string
|
||||
* @param fromToOnSameDay TRUE iff from and to dates are on the same day
|
||||
* (any difference is in ampm/hours or below)
|
||||
* @param appendTo Output parameter to receive result.
|
||||
* Result is appended to existing contents.
|
||||
* @param pos On input: an alignment field, if desired.
|
||||
|
@ -684,6 +703,7 @@ private:
|
|||
*/
|
||||
UnicodeString& fallbackFormat(Calendar& fromCalendar,
|
||||
Calendar& toCalendar,
|
||||
UBool fromToOnSameDay,
|
||||
UnicodeString& appendTo,
|
||||
FieldPosition& pos,
|
||||
UErrorCode& status) const;
|
||||
|
@ -874,13 +894,11 @@ private:
|
|||
* both time and date. Present the date followed by
|
||||
* the range expression for the time.
|
||||
* @param format date and time format
|
||||
* @param formatLen format string length
|
||||
* @param datePattern date pattern
|
||||
* @param field time calendar field: AM_PM, HOUR, MINUTE
|
||||
* @param status output param set to success/failure code on exit
|
||||
*/
|
||||
void concatSingleDate2TimeInterval(const UChar* format,
|
||||
int32_t formatLen,
|
||||
void concatSingleDate2TimeInterval(UnicodeString& format,
|
||||
const UnicodeString& datePattern,
|
||||
UCalendarDateFields field,
|
||||
UErrorCode& status);
|
||||
|
@ -966,10 +984,17 @@ private:
|
|||
DateTimePatternGenerator* fDtpng;
|
||||
|
||||
/**
|
||||
* Following are interval information relavent (locale) to this formatter.
|
||||
* Following are interval information relevant (locale) to this formatter.
|
||||
*/
|
||||
UnicodeString fSkeleton;
|
||||
PatternInfo fIntervalPatterns[DateIntervalInfo::kIPI_MAX_INDEX];
|
||||
|
||||
/**
|
||||
* Patterns for fallback formatting.
|
||||
*/
|
||||
UnicodeString* fDatePattern;
|
||||
UnicodeString* fTimePattern;
|
||||
UnicodeString* fDateTimeFormat;
|
||||
};
|
||||
|
||||
inline UBool
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2008-2014, International Business Machines Corporation and
|
||||
* Copyright (C) 2008-2015, International Business Machines Corporation and
|
||||
* others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
*
|
||||
|
@ -353,6 +353,7 @@ private:
|
|||
kIPI_AM_PM,
|
||||
kIPI_HOUR,
|
||||
kIPI_MINUTE,
|
||||
kIPI_SECOND,
|
||||
kIPI_MAX_INDEX
|
||||
};
|
||||
public:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/********************************************************************
|
||||
* Copyright (c) 2011-2014, International Business Machines Corporation
|
||||
* Copyright (c) 2011-2015, International Business Machines Corporation
|
||||
* and others. All Rights Reserved.
|
||||
********************************************************************/
|
||||
/* C API TEST FOR DATE INTERVAL FORMAT */
|
||||
|
@ -16,6 +16,7 @@
|
|||
#include "cmemory.h"
|
||||
|
||||
static void TestDateIntervalFormat(void);
|
||||
static void TestFPos_SkelWithSeconds(void);
|
||||
|
||||
#define LEN(a) (sizeof(a)/sizeof(a[0]))
|
||||
|
||||
|
@ -26,6 +27,7 @@ void addDateIntervalFormatTest(TestNode** root);
|
|||
void addDateIntervalFormatTest(TestNode** root)
|
||||
{
|
||||
TESTCASE(TestDateIntervalFormat);
|
||||
TESTCASE(TestFPos_SkelWithSeconds);
|
||||
}
|
||||
|
||||
static const char tzUSPacific[] = "US/Pacific";
|
||||
|
@ -114,4 +116,205 @@ static void TestDateIntervalFormat()
|
|||
ctest_resetTimeZone();
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* TestFPos_SkelWithSeconds and related data
|
||||
********************************************************************
|
||||
*/
|
||||
|
||||
static UChar zoneGMT[] = { 0x47,0x4D,0x54,0 }; // GMT
|
||||
static const UDate startTime = 1416474000000.0; // 2014 Nov 20 09:00 GMT
|
||||
|
||||
static const double deltas[] = {
|
||||
0.0, // none
|
||||
200.0, // 200 millisec
|
||||
20000.0, // 20 sec
|
||||
1200000.0, // 20 min
|
||||
7200000.0, // 2 hrs
|
||||
43200000.0, // 12 hrs
|
||||
691200000.0, // 8 days
|
||||
1382400000.0, // 16 days,
|
||||
8640000000.0, // 100 days
|
||||
-1.0
|
||||
};
|
||||
enum { kNumDeltas = sizeof(deltas)/sizeof(deltas[0]) - 1 };
|
||||
|
||||
typedef struct {
|
||||
int32_t posBegin;
|
||||
int32_t posEnd;
|
||||
const char * format;
|
||||
} ExpectPosAndFormat;
|
||||
|
||||
static const ExpectPosAndFormat exp_en_HHmm[kNumDeltas] = {
|
||||
{ 3, 5, "09:00" },
|
||||
{ 3, 5, "09:00" },
|
||||
{ 3, 5, "09:00" },
|
||||
{ 3, 5, "09:00 \\u2013 09:20" },
|
||||
{ 3, 5, "09:00 \\u2013 11:00" },
|
||||
{ 3, 5, "09:00 \\u2013 21:00" },
|
||||
{ 15, 17, "11/20/2014, 09:00 \\u2013 11/28/2014, 09:00" },
|
||||
{ 15, 17, "11/20/2014, 09:00 \\u2013 12/6/2014, 09:00" },
|
||||
{ 15, 17, "11/20/2014, 09:00 \\u2013 2/28/2015, 09:00" }
|
||||
};
|
||||
|
||||
static const ExpectPosAndFormat exp_en_HHmmss[kNumDeltas] = {
|
||||
{ 3, 5, "09:00:00" },
|
||||
{ 3, 5, "09:00:00" },
|
||||
{ 3, 5, "09:00:00 \\u2013 09:00:20" },
|
||||
{ 3, 5, "09:00:00 \\u2013 09:20:00" },
|
||||
{ 3, 5, "09:00:00 \\u2013 11:00:00" },
|
||||
{ 3, 5, "09:00:00 \\u2013 21:00:00" },
|
||||
{ 15, 17, "11/20/2014, 09:00:00 \\u2013 11/28/2014, 09:00:00" },
|
||||
{ 15, 17, "11/20/2014, 09:00:00 \\u2013 12/6/2014, 09:00:00" },
|
||||
{ 15, 17, "11/20/2014, 09:00:00 \\u2013 2/28/2015, 09:00:00" }
|
||||
};
|
||||
|
||||
static const ExpectPosAndFormat exp_en_yyMMdd[kNumDeltas] = {
|
||||
{ 0, 0, "11/20/14" },
|
||||
{ 0, 0, "11/20/14" },
|
||||
{ 0, 0, "11/20/14" },
|
||||
{ 0, 0, "11/20/14" },
|
||||
{ 0, 0, "11/20/14" },
|
||||
{ 0, 0, "11/20/14" },
|
||||
{ 0, 0, "11/20/14 \\u2013 11/28/14" },
|
||||
{ 0, 0, "11/20/14 \\u2013 12/6/14" },
|
||||
{ 0, 0, "11/20/14 \\u2013 2/28/15" }
|
||||
};
|
||||
|
||||
static const ExpectPosAndFormat exp_en_yyMMddHHmm[kNumDeltas] = {
|
||||
{ 13, 15, "11/20/14, 09:00" },
|
||||
{ 13, 15, "11/20/14, 09:00" },
|
||||
{ 13, 15, "11/20/14, 09:00" },
|
||||
{ 13, 15, "11/20/14, 09:00 \\u2013 09:20" },
|
||||
{ 13, 15, "11/20/14, 09:00 \\u2013 11:00" },
|
||||
{ 13, 15, "11/20/14, 09:00 \\u2013 21:00" },
|
||||
{ 13, 15, "11/20/14, 09:00 \\u2013 11/28/14, 09:00" },
|
||||
{ 13, 15, "11/20/14, 09:00 \\u2013 12/06/14, 09:00" },
|
||||
{ 13, 15, "11/20/14, 09:00 \\u2013 02/28/15, 09:00" }
|
||||
};
|
||||
|
||||
static const ExpectPosAndFormat exp_en_yyMMddHHmmss[kNumDeltas] = {
|
||||
{ 13, 15, "11/20/14, 09:00:00" },
|
||||
{ 13, 15, "11/20/14, 09:00:00" },
|
||||
{ 13, 15, "11/20/14, 09:00:00 \\u2013 09:00:20" },
|
||||
{ 13, 15, "11/20/14, 09:00:00 \\u2013 09:20:00" },
|
||||
{ 13, 15, "11/20/14, 09:00:00 \\u2013 11:00:00" },
|
||||
{ 13, 15, "11/20/14, 09:00:00 \\u2013 21:00:00" },
|
||||
{ 13, 15, "11/20/14, 09:00:00 \\u2013 11/28/14, 09:00:00" },
|
||||
{ 13, 15, "11/20/14, 09:00:00 \\u2013 12/06/14, 09:00:00" },
|
||||
{ 13, 15, "11/20/14, 09:00:00 \\u2013 02/28/15, 09:00:00" }
|
||||
};
|
||||
|
||||
static const ExpectPosAndFormat exp_en_yMMMdhmmssz[kNumDeltas] = {
|
||||
{ 16, 18, "Nov 20, 2014, 9:00:00 AM GMT" },
|
||||
{ 16, 18, "Nov 20, 2014, 9:00:00 AM GMT" },
|
||||
{ 16, 18, "Nov 20, 2014, 9:00:00 AM GMT \\u2013 9:00:20 AM GMT" },
|
||||
{ 16, 18, "Nov 20, 2014, 9:00:00 AM GMT \\u2013 9:20:00 AM GMT" },
|
||||
{ 16, 18, "Nov 20, 2014, 9:00:00 AM GMT \\u2013 11:00:00 AM GMT" },
|
||||
{ 16, 18, "Nov 20, 2014, 9:00:00 AM GMT \\u2013 9:00:00 PM GMT" },
|
||||
{ 16, 18, "Nov 20, 2014, 9:00:00 AM GMT \\u2013 Nov 28, 2014, 9:00:00 AM GMT" },
|
||||
{ 16, 18, "Nov 20, 2014, 9:00:00 AM GMT \\u2013 Dec 6, 2014, 9:00:00 AM GMT" },
|
||||
{ 16, 18, "Nov 20, 2014, 9:00:00 AM GMT \\u2013 Feb 28, 2015, 9:00:00 AM GMT" }
|
||||
};
|
||||
|
||||
static const ExpectPosAndFormat exp_ja_yyMMddHHmm[kNumDeltas] = {
|
||||
{ 11, 13, "14/11/20 9:00" },
|
||||
{ 11, 13, "14/11/20 9:00" },
|
||||
{ 11, 13, "14/11/20 9:00" },
|
||||
{ 11, 13, "14/11/20 9\\u664200\\u5206\\uFF5E9\\u664220\\u5206" },
|
||||
{ 11, 13, "14/11/20 9\\u664200\\u5206\\uFF5E11\\u664200\\u5206" },
|
||||
{ 11, 13, "14/11/20 9\\u664200\\u5206\\uFF5E21\\u664200\\u5206" },
|
||||
{ 11, 13, "14/11/20 9:00\\uFF5E14/11/28 9:00" },
|
||||
{ 11, 13, "14/11/20 9:00\\uFF5E14/12/06 9:00" },
|
||||
{ 11, 13, "14/11/20 9:00\\uFF5E15/02/28 9:00" }
|
||||
};
|
||||
|
||||
static const ExpectPosAndFormat exp_ja_yyMMddHHmmss[kNumDeltas] = {
|
||||
{ 11, 13, "14/11/20 9:00:00" },
|
||||
{ 11, 13, "14/11/20 9:00:00" },
|
||||
{ 11, 13, "14/11/20 9:00:00\\uFF5E9:00:20" },
|
||||
{ 11, 13, "14/11/20 9:00:00\\uFF5E9:20:00" },
|
||||
{ 11, 13, "14/11/20 9:00:00\\uFF5E11:00:00" },
|
||||
{ 11, 13, "14/11/20 9:00:00\\uFF5E21:00:00" },
|
||||
{ 11, 13, "14/11/20 9:00:00\\uFF5E14/11/28 9:00:00" },
|
||||
{ 11, 13, "14/11/20 9:00:00\\uFF5E14/12/06 9:00:00" },
|
||||
{ 11, 13, "14/11/20 9:00:00\\uFF5E15/02/28 9:00:00" }
|
||||
};
|
||||
|
||||
static const ExpectPosAndFormat exp_ja_yMMMdHHmmss[kNumDeltas] = {
|
||||
{ 14, 16, "2014\\u5E7411\\u670820\\u65E5 9:00:00" },
|
||||
{ 14, 16, "2014\\u5E7411\\u670820\\u65E5 9:00:00" },
|
||||
{ 14, 16, "2014\\u5E7411\\u670820\\u65E5 9:00:00\\uFF5E9:00:20" },
|
||||
{ 14, 16, "2014\\u5E7411\\u670820\\u65E5 9:00:00\\uFF5E9:20:00" },
|
||||
{ 14, 16, "2014\\u5E7411\\u670820\\u65E5 9:00:00\\uFF5E11:00:00" },
|
||||
{ 14, 16, "2014\\u5E7411\\u670820\\u65E5 9:00:00\\uFF5E21:00:00" },
|
||||
{ 14, 16, "2014\\u5E7411\\u670820\\u65E5 9:00:00\\uFF5E2014\\u5E7411\\u670828\\u65E5 9:00:00" },
|
||||
{ 14, 16, "2014\\u5E7411\\u670820\\u65E5 9:00:00\\uFF5E2014\\u5E7412\\u67086\\u65E5 9:00:00" },
|
||||
{ 14, 16, "2014\\u5E7411\\u670820\\u65E5 9:00:00\\uFF5E2015\\u5E742\\u670828\\u65E5 9:00:00" }
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const char * locale;
|
||||
const char * skeleton;
|
||||
UDateFormatField fieldToCheck;
|
||||
const ExpectPosAndFormat * expected;
|
||||
} LocaleAndSkeletonItem;
|
||||
|
||||
static const LocaleAndSkeletonItem locSkelItems[] = {
|
||||
{ "en", "HHmm", UDAT_MINUTE_FIELD, exp_en_HHmm },
|
||||
{ "en", "HHmmss", UDAT_MINUTE_FIELD, exp_en_HHmmss },
|
||||
{ "en", "yyMMdd", UDAT_MINUTE_FIELD, exp_en_yyMMdd },
|
||||
{ "en", "yyMMddHHmm", UDAT_MINUTE_FIELD, exp_en_yyMMddHHmm },
|
||||
{ "en", "yyMMddHHmmss", UDAT_MINUTE_FIELD, exp_en_yyMMddHHmmss },
|
||||
{ "en", "yMMMdhmmssz", UDAT_MINUTE_FIELD, exp_en_yMMMdhmmssz },
|
||||
{ "ja", "yyMMddHHmm", UDAT_MINUTE_FIELD, exp_ja_yyMMddHHmm },
|
||||
{ "ja", "yyMMddHHmmss", UDAT_MINUTE_FIELD, exp_ja_yyMMddHHmmss },
|
||||
{ "ja", "yMMMdHHmmss", UDAT_MINUTE_FIELD, exp_ja_yMMMdHHmmss },
|
||||
{ NULL, NULL, (UDateFormatField)0, NULL }
|
||||
};
|
||||
|
||||
enum { kSizeUBuf = 96, kSizeBBuf = 192 };
|
||||
|
||||
static void TestFPos_SkelWithSeconds()
|
||||
{
|
||||
const LocaleAndSkeletonItem * locSkelItemPtr;
|
||||
for (locSkelItemPtr = locSkelItems; locSkelItemPtr->locale != NULL; locSkelItemPtr++) {
|
||||
UDateIntervalFormat* udifmt;
|
||||
UChar ubuf[kSizeUBuf];
|
||||
int32_t ulen, uelen;
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
||||
u_strFromUTF8(ubuf, kSizeUBuf, &ulen, locSkelItemPtr->skeleton, -1, &status);
|
||||
udifmt = udtitvfmt_open(locSkelItemPtr->locale, ubuf, ulen, zoneGMT, -1, &status);
|
||||
if ( U_FAILURE(status) ) {
|
||||
log_data_err("FAIL: udtitvfmt_open for locale %s, skeleton %s: %s\n",
|
||||
locSkelItemPtr->locale, locSkelItemPtr->skeleton, u_errorName(status));
|
||||
} else {
|
||||
const double * deltasPtr = deltas;
|
||||
const ExpectPosAndFormat * expectedPtr = locSkelItemPtr->expected;
|
||||
for (; *deltasPtr >= 0.0; deltasPtr++, expectedPtr++) {
|
||||
UFieldPosition fpos = { locSkelItemPtr->fieldToCheck, 0, 0 };
|
||||
UChar uebuf[kSizeUBuf];
|
||||
char bbuf[kSizeBBuf];
|
||||
char bebuf[kSizeBBuf];
|
||||
status = U_ZERO_ERROR;
|
||||
uelen = u_unescape(expectedPtr->format, uebuf, kSizeUBuf);
|
||||
ulen = udtitvfmt_format(udifmt, startTime, startTime + *deltasPtr, ubuf, kSizeUBuf, &fpos, &status);
|
||||
if ( U_FAILURE(status) ) {
|
||||
log_err("FAIL: udtitvfmt_format for locale %s, skeleton %s, delta %.1f: %s\n",
|
||||
locSkelItemPtr->locale, locSkelItemPtr->skeleton, *deltasPtr, u_errorName(status));
|
||||
} else if ( ulen != uelen || u_strncmp(ubuf,uebuf,uelen) != 0 ||
|
||||
fpos.beginIndex != expectedPtr->posBegin || fpos.endIndex != expectedPtr->posEnd ) {
|
||||
u_strToUTF8(bbuf, kSizeBBuf, NULL, ubuf, ulen, &status);
|
||||
u_strToUTF8(bebuf, kSizeBBuf, NULL, uebuf, uelen, &status); // convert back to get unescaped string
|
||||
log_err("FAIL: udtitvfmt_format for locale %s, skeleton %s, delta %12.1f, expect %d-%d \"%s\", get %d-%d \"%s\"\n",
|
||||
locSkelItemPtr->locale, locSkelItemPtr->skeleton, *deltasPtr,
|
||||
expectedPtr->posBegin, expectedPtr->posEnd, bebuf,
|
||||
fpos.beginIndex, fpos.endIndex, bbuf);
|
||||
}
|
||||
}
|
||||
udtitvfmt_close(udifmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
|
Loading…
Add table
Reference in a new issue