/*******************************************************************************
-* Copyright (C) 2008, International Business Machines Corporation and
+* Copyright (C) 2008-2016, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*
*******************************************************************************
*/
+#include "utypeinfo.h" // for 'typeid' to work
+
#include "unicode/dtitvfmt.h"
#if !UCONFIG_NO_FORMATTING
-//FIXME: put in compilation
+//TODO: put in compilation
//#define DTITVFMT_DEBUG 1
-#include "cstring.h"
-#include "unicode/msgfmt.h"
+#include "unicode/calendar.h"
#include "unicode/dtptngen.h"
#include "unicode/dtitvinf.h"
-#include "unicode/calendar.h"
+#include "unicode/udateintervalformat.h"
+#include "unicode/simpleformatter.h"
+#include "cmemory.h"
+#include "cstring.h"
#include "dtitv_impl.h"
+#include "gregoimp.h"
+#include "mutex.h"
#ifdef DTITVFMT_DEBUG
#include <iostream>
-#include "cstring.h"
#endif
-#include "gregoimp.h"
-
U_NAMESPACE_BEGIN
// latestFirst:
-static const UChar gLaterFirstPrefix[] = {LOW_L, LOW_A, LOW_T, LOW_E, LOW_S,LOW_T, CAP_F, LOW_I, LOW_R, LOW_S, LOW_T, COLON, 0};
+static const UChar gLaterFirstPrefix[] = {LOW_L, LOW_A, LOW_T, LOW_E, LOW_S,LOW_T, CAP_F, LOW_I, LOW_R, LOW_S, LOW_T, COLON};
// earliestFirst:
-static const UChar gEarlierFirstPrefix[] = {LOW_E, LOW_A, LOW_R, LOW_L, LOW_I, LOW_E, LOW_S, LOW_T, CAP_F, LOW_I, LOW_R, LOW_S, LOW_T, COLON, 0};
+static const UChar gEarlierFirstPrefix[] = {LOW_E, LOW_A, LOW_R, LOW_L, LOW_I, LOW_E, LOW_S, LOW_T, CAP_F, LOW_I, LOW_R, LOW_S, LOW_T, COLON};
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalFormat)
+// Mutex, protects access to fDateFormat, fFromCalendar and fToCalendar.
+// Needed because these data members are modified by const methods of DateIntervalFormat.
+static UMutex gFormatterMutex = U_MUTEX_INITIALIZER;
DateIntervalFormat* U_EXPORT2
DateIntervalFormat::createInstance(const UnicodeString& skeleton,
DateIntervalFormat::createInstance(const UnicodeString& skeleton,
const Locale& locale,
UErrorCode& status) {
- DateFormat* dtfmt = DateFormat::createPatternInstance(skeleton, locale, status);
-
#ifdef DTITVFMT_DEBUG
char result[1000];
char result_1[1000];
#endif
DateIntervalInfo* dtitvinf = new DateIntervalInfo(locale, status);
- return create(dtfmt, dtitvinf, &skeleton, status);
+ return create(locale, dtitvinf, &skeleton, status);
}
const Locale& locale,
const DateIntervalInfo& dtitvinf,
UErrorCode& status) {
- DateFormat* dtfmt = DateFormat::createPatternInstance(skeleton, locale, status);
DateIntervalInfo* ptn = dtitvinf.clone();
- return create(dtfmt, ptn, &skeleton, status);
+ return create(locale, ptn, &skeleton, status);
}
: fInfo(NULL),
fDateFormat(NULL),
fFromCalendar(NULL),
- fToCalendar(NULL)
+ fToCalendar(NULL),
+ fLocale(Locale::getRoot()),
+ fDatePattern(NULL),
+ fTimePattern(NULL),
+ fDateTimeFormat(NULL),
+ fMinimizeType(UDTITVFMT_MINIMIZE_NONE)
{}
fInfo(NULL),
fDateFormat(NULL),
fFromCalendar(NULL),
- fToCalendar(NULL) {
+ fToCalendar(NULL),
+ fLocale(itvfmt.fLocale),
+ fDatePattern(NULL),
+ fTimePattern(NULL),
+ fDateTimeFormat(NULL),
+ fMinimizeType(UDTITVFMT_MINIMIZE_NONE) {
*this = itvfmt;
}
delete fInfo;
delete fFromCalendar;
delete fToCalendar;
- if ( itvfmt.fDateFormat ) {
- fDateFormat = (SimpleDateFormat*)itvfmt.fDateFormat->clone();
- } else {
- fDateFormat = NULL;
+ delete fDatePattern;
+ delete fTimePattern;
+ delete fDateTimeFormat;
+ {
+ Mutex lock(&gFormatterMutex);
+ if ( itvfmt.fDateFormat ) {
+ fDateFormat = (SimpleDateFormat*)itvfmt.fDateFormat->clone();
+ } else {
+ fDateFormat = NULL;
+ }
+ if ( itvfmt.fFromCalendar ) {
+ fFromCalendar = itvfmt.fFromCalendar->clone();
+ } else {
+ fFromCalendar = NULL;
+ }
+ if ( itvfmt.fToCalendar ) {
+ fToCalendar = itvfmt.fToCalendar->clone();
+ } else {
+ fToCalendar = NULL;
+ }
}
if ( itvfmt.fInfo ) {
fInfo = itvfmt.fInfo->clone();
} else {
fInfo = NULL;
}
- if ( itvfmt.fFromCalendar ) {
- fFromCalendar = itvfmt.fFromCalendar->clone();
- } else {
- fFromCalendar = NULL;
- }
- if ( itvfmt.fToCalendar ) {
- fToCalendar = itvfmt.fToCalendar->clone();
- } else {
- fToCalendar = NULL;
- }
fSkeleton = itvfmt.fSkeleton;
int8_t i;
for ( i = 0; i< DateIntervalInfo::kIPI_MAX_INDEX; ++i ) {
fIntervalPatterns[i] = itvfmt.fIntervalPatterns[i];
}
+ fLocale = itvfmt.fLocale;
+ 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;
}
delete fDateFormat;
delete fFromCalendar;
delete fToCalendar;
+ delete fDatePattern;
+ delete fTimePattern;
+ delete fDateTimeFormat;
}
UBool
DateIntervalFormat::operator==(const Format& other) const {
- if ( other.getDynamicClassID() == DateIntervalFormat::getStaticClassID() ) {
- DateIntervalFormat* fmt = (DateIntervalFormat*)&other;
-#ifdef DTITVFMT_DEBUG
- UBool equal;
- equal = (this == fmt);
-
- equal = (*fInfo == *fmt->fInfo);
- equal = (*fDateFormat == *fmt->fDateFormat);
- equal = fFromCalendar->isEquivalentTo(*fmt->fFromCalendar) ;
- equal = fToCalendar->isEquivalentTo(*fmt->fToCalendar) ;
- equal = (fSkeleton == fmt->fSkeleton);
-#endif
- UBool res;
- res = ( this == fmt ) ||
- ( Format::operator==(other) &&
- fInfo &&
- ( *fInfo == *fmt->fInfo ) &&
- fDateFormat &&
- ( *fDateFormat == *fmt->fDateFormat ) &&
- fFromCalendar &&
- fFromCalendar->isEquivalentTo(*fmt->fFromCalendar) &&
- fToCalendar &&
- fToCalendar->isEquivalentTo(*fmt->fToCalendar) &&
- fSkeleton == fmt->fSkeleton );
- int8_t i;
- for (i = 0; i< DateIntervalInfo::kIPI_MAX_INDEX && res == TRUE; ++i ) {
- res = ( fIntervalPatterns[i].firstPart ==
- fmt->fIntervalPatterns[i].firstPart) &&
- ( fIntervalPatterns[i].secondPart ==
- fmt->fIntervalPatterns[i].secondPart ) &&
- ( fIntervalPatterns[i].laterDateFirst ==
- fmt->fIntervalPatterns[i].laterDateFirst) ;
- }
- return res;
- }
- return FALSE;
-}
+ if (typeid(*this) != typeid(other)) {return FALSE;}
+ const DateIntervalFormat* fmt = (DateIntervalFormat*)&other;
+ if (this == fmt) {return TRUE;}
+ if (!Format::operator==(other)) {return FALSE;}
+ if ((fInfo != fmt->fInfo) && (fInfo == NULL || fmt->fInfo == NULL)) {return FALSE;}
+ if (fInfo && fmt->fInfo && (*fInfo != *fmt->fInfo )) {return FALSE;}
+ {
+ Mutex lock(&gFormatterMutex);
+ if (fDateFormat != fmt->fDateFormat && (fDateFormat == NULL || fmt->fDateFormat == NULL)) {return FALSE;}
+ if (fDateFormat && fmt->fDateFormat && (*fDateFormat != *fmt->fDateFormat)) {return FALSE;}
+ // TODO: should operator == ignore the From and ToCalendar? They hold transient values during
+ // formatting of a DateInterval.
+ if (fFromCalendar != fmt->fFromCalendar && (fFromCalendar == NULL || fmt->fFromCalendar == NULL)) {return FALSE;}
+ if (fFromCalendar && fmt->fFromCalendar && !fFromCalendar->isEquivalentTo(*fmt->fFromCalendar)) {return FALSE;}
+
+ if (fToCalendar != fmt->fToCalendar && (fToCalendar == NULL || fmt->fToCalendar == NULL)) {return FALSE;}
+ if (fToCalendar && fmt->fToCalendar && !fToCalendar->isEquivalentTo(*fmt->fToCalendar)) {return FALSE;}
+ }
+ if (fSkeleton != fmt->fSkeleton) {return FALSE;}
+ if (fDatePattern != fmt->fDatePattern && (fDatePattern == NULL || fmt->fDatePattern == NULL)) {return FALSE;}
+ if (fDatePattern && fmt->fDatePattern && (*fDatePattern != *fmt->fDatePattern)) {return FALSE;}
+ if (fTimePattern != fmt->fTimePattern && (fTimePattern == NULL || fmt->fTimePattern == NULL)) {return FALSE;}
+ if (fTimePattern && fmt->fTimePattern && (*fTimePattern != *fmt->fTimePattern)) {return FALSE;}
+ if (fDateTimeFormat != fmt->fDateTimeFormat && (fDateTimeFormat == NULL || fmt->fDateTimeFormat == NULL)) {return FALSE;}
+ if (fDateTimeFormat && fmt->fDateTimeFormat && (*fDateTimeFormat != *fmt->fDateTimeFormat)) {return FALSE;}
+ if (fLocale != fmt->fLocale) {return FALSE;}
+
+ for (int32_t i = 0; i< DateIntervalInfo::kIPI_MAX_INDEX; ++i ) {
+ if (fIntervalPatterns[i].firstPart != fmt->fIntervalPatterns[i].firstPart) {return FALSE;}
+ if (fIntervalPatterns[i].secondPart != fmt->fIntervalPatterns[i].secondPart ) {return FALSE;}
+ if (fIntervalPatterns[i].laterDateFirst != fmt->fIntervalPatterns[i].laterDateFirst) {return FALSE;}
+ }
+ return TRUE;
+}
UnicodeString&
if ( obj.getType() == Formattable::kObject ) {
const UObject* formatObj = obj.getObject();
- if (formatObj->getDynamicClassID() == DateInterval::getStaticClassID()){
- return format((DateInterval*)formatObj, appendTo, fieldPosition, status);
+ const DateInterval* interval = dynamic_cast<const DateInterval*>(formatObj);
+ if (interval != NULL) {
+ return format(interval, appendTo, fieldPosition, status);
}
}
status = U_ILLEGAL_ARGUMENT_ERROR;
if ( U_FAILURE(status) ) {
return appendTo;
}
-
- if ( fFromCalendar != NULL && fToCalendar != NULL &&
- fDateFormat != NULL && fInfo != NULL ) {
- fFromCalendar->setTime(dtInterval->getFromDate(), status);
- fToCalendar->setTime(dtInterval->getToDate(), status);
- if ( U_SUCCESS(status) ) {
- return format(*fFromCalendar, *fToCalendar, appendTo,fieldPosition, status);
- }
+ if (fFromCalendar == NULL || fToCalendar == NULL || fDateFormat == NULL || fInfo == NULL) {
+ status = U_INVALID_STATE_ERROR;
+ return appendTo;
}
- return appendTo;
+
+ Mutex lock(&gFormatterMutex);
+ fFromCalendar->setTime(dtInterval->getFromDate(), status);
+ fToCalendar->setTime(dtInterval->getToDate(), status);
+ return formatImpl(*fFromCalendar, *fToCalendar, appendTo,fieldPosition, status);
}
UnicodeString& appendTo,
FieldPosition& pos,
UErrorCode& status) const {
+ Mutex lock(&gFormatterMutex);
+ return formatImpl(fromCalendar, toCalendar, appendTo, pos, status);
+}
+
+
+UnicodeString&
+DateIntervalFormat::formatImpl(Calendar& fromCalendar,
+ Calendar& toCalendar,
+ UnicodeString& appendTo,
+ FieldPosition& pos,
+ UErrorCode& status) const {
if ( U_FAILURE(status) ) {
return appendTo;
}
// not support different calendar types and time zones
//if ( fromCalendar.getType() != toCalendar.getType() ) {
- if ( !fromCalendar.isEquivalentTo(toCalendar) ||
- uprv_strcmp(fromCalendar.getType(), "gregorian") ) {
+ if ( !fromCalendar.isEquivalentTo(toCalendar) ) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return appendTo;
}
// First, find the largest different calendar field.
UCalendarDateFields field = UCAL_FIELD_COUNT;
+ UChar patternDay = 0x0064; // d
+ UChar patternYear = 0x0079; // y
if ( fromCalendar.get(UCAL_ERA,status) != toCalendar.get(UCAL_ERA,status)) {
field = UCAL_ERA;
} else if ( fromCalendar.get(UCAL_YEAR, status) !=
toCalendar.get(UCAL_YEAR, status) ) {
field = UCAL_YEAR;
+ if (fMinimizeType == UDTITVFMT_MINIMIZE_ADJACENT_MONTHS && fSkeleton.indexOf(patternDay) >= 0 && fSkeleton.indexOf(patternYear) < 0) {
+ UDate fromDate = fromCalendar.getTime(status);
+ UDate toDate = toCalendar.getTime(status);
+ int32_t fromDay = fromCalendar.get(UCAL_DATE, status);
+ int32_t toDay = toCalendar.get(UCAL_DATE, status);
+ fromCalendar.add(UCAL_MONTH, 1, status);
+ if ( fromDate < toDate && fromCalendar.getTime(status) > toDate && fromDay > toDay ) {
+ field = UCAL_DATE;
+ }
+ fromCalendar.setTime(fromDate, status);
+ }
} else if ( fromCalendar.get(UCAL_MONTH, status) !=
toCalendar.get(UCAL_MONTH, status) ) {
field = UCAL_MONTH;
+ if (fMinimizeType == UDTITVFMT_MINIMIZE_ADJACENT_MONTHS && fSkeleton.indexOf(patternDay) >= 0) {
+ UDate fromDate = fromCalendar.getTime(status);
+ UDate toDate = toCalendar.getTime(status);
+ int32_t fromDay = fromCalendar.get(UCAL_DATE, status);
+ int32_t toDay = toCalendar.get(UCAL_DATE, status);
+ fromCalendar.add(UCAL_MONTH, 1, status);
+ if ( fromDate < toDate && fromCalendar.getTime(status) > toDate && fromDay > toDay ) {
+ field = UCAL_DATE;
+ }
+ fromCalendar.setTime(fromDate, status);
+ }
} else if ( fromCalendar.get(UCAL_DATE, status) !=
toCalendar.get(UCAL_DATE, status) ) {
field = UCAL_DATE;
+ if (fMinimizeType == UDTITVFMT_MINIMIZE_ADJACENT_DAYS &&
+ // check normalized skeleton for 'H', 'h', 'j'
+ (fSkeleton.indexOf(0x0048) >= 0 || fSkeleton.indexOf(0x0068) >= 0 || fSkeleton.indexOf(0x006A) >= 0)) {
+ UDate fromDate = fromCalendar.getTime(status);
+ UDate toDate = toCalendar.getTime(status);
+ int32_t fromHour = fromCalendar.get(UCAL_HOUR, status);
+ int32_t toHour = toCalendar.get(UCAL_HOUR, status);
+ fromCalendar.add(UCAL_HOUR_OF_DAY, 12, status);
+ if ( fromDate < toDate && fromCalendar.getTime(status) > toDate && fromHour > toHour ) {
+ field = UCAL_AM_PM;
+ }
+ fromCalendar.setTime(fromDate, status);
+ }
} else if ( fromCalendar.get(UCAL_AM_PM, status) !=
toCalendar.get(UCAL_AM_PM, status) ) {
field = UCAL_AM_PM;
} 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
*/
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.
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;
}
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;
UErrorCode& status) {
delete fInfo;
fInfo = new DateIntervalInfo(newItvPattern);
- if ( fDateFormat ) {
+
+ // Delete patterns that get reset by initializePattern
+ delete fDatePattern;
+ fDatePattern = NULL;
+ delete fTimePattern;
+ fTimePattern = NULL;
+ delete fDateTimeFormat;
+ fDateTimeFormat = NULL;
+
+ if (fDateFormat) {
initializePattern(status);
}
}
}
-DateIntervalFormat::DateIntervalFormat(DateFormat* dtfmt,
- DateIntervalInfo* dtItvInfo,
- const UnicodeString* skeleton,
- UErrorCode& status)
-: fInfo(0),
- fDateFormat(0),
- fFromCalendar(0),
- fToCalendar(0)
+void
+DateIntervalFormat::adoptTimeZone(TimeZone* zone)
+{
+ if (fDateFormat != NULL) {
+ fDateFormat->adoptTimeZone(zone);
+ }
+ // The fDateFormat has the master calendar for the DateIntervalFormat and has
+ // ownership of any adopted TimeZone; fFromCalendar and fToCalendar are internal
+ // work clones of that calendar (and should not also be given ownership of the
+ // adopted TimeZone).
+ if (fFromCalendar) {
+ fFromCalendar->setTimeZone(*zone);
+ }
+ if (fToCalendar) {
+ fToCalendar->setTimeZone(*zone);
+ }
+}
+
+void
+DateIntervalFormat::setTimeZone(const TimeZone& zone)
+{
+ if (fDateFormat != NULL) {
+ fDateFormat->setTimeZone(zone);
+ }
+ // The fDateFormat has the master calendar for the DateIntervalFormat;
+ // fFromCalendar and fToCalendar are internal work clones of that calendar.
+ if (fFromCalendar) {
+ fFromCalendar->setTimeZone(zone);
+ }
+ if (fToCalendar) {
+ fToCalendar->setTimeZone(zone);
+ }
+}
+
+const TimeZone&
+DateIntervalFormat::getTimeZone() const
+{
+ if (fDateFormat != NULL) {
+ Mutex lock(&gFormatterMutex);
+ return fDateFormat->getTimeZone();
+ }
+ // If fDateFormat is NULL (unexpected), create default timezone.
+ return *(TimeZone::createDefault());
+}
+
+void
+DateIntervalFormat::setAttribute(UDateIntervalFormatAttribute attr,
+ UDateIntervalFormatAttributeValue value,
+ UErrorCode &status)
{
if ( U_FAILURE(status) ) {
- delete dtfmt;
- delete dtItvInfo;
return;
}
- if ( dtfmt == NULL || dtItvInfo == NULL ) {
- status = U_MEMORY_ALLOCATION_ERROR;
- // safe to delete NULL
- delete dtfmt;
- delete dtItvInfo;
+ if (attr == UDTITVFMT_MINIMIZE_TYPE) {
+ fMinimizeType = value;
+ } else {
+ status = U_ILLEGAL_ARGUMENT_ERROR;
+ }
+}
+
+DateIntervalFormat::DateIntervalFormat(const Locale& locale,
+ DateIntervalInfo* dtItvInfo,
+ const UnicodeString* skeleton,
+ UErrorCode& status)
+: fInfo(NULL),
+ fDateFormat(NULL),
+ fFromCalendar(NULL),
+ fToCalendar(NULL),
+ fLocale(locale),
+ fDatePattern(NULL),
+ fTimePattern(NULL),
+ fDateTimeFormat(NULL),
+ fMinimizeType(UDTITVFMT_MINIMIZE_NONE)
+{
+ LocalPointer<DateIntervalInfo> info(dtItvInfo, status);
+ LocalPointer<SimpleDateFormat> dtfmt(static_cast<SimpleDateFormat *>(
+ DateFormat::createInstanceForSkeleton(*skeleton, locale, status)), status);
+ if (U_FAILURE(status)) {
return;
}
+
if ( skeleton ) {
fSkeleton = *skeleton;
}
- fInfo = dtItvInfo;
- fDateFormat = (SimpleDateFormat*)dtfmt;
- if ( dtfmt->getCalendar() ) {
- fFromCalendar = dtfmt->getCalendar()->clone();
- fToCalendar = dtfmt->getCalendar()->clone();
- } else {
- fFromCalendar = NULL;
- fToCalendar = NULL;
+ fInfo = info.orphan();
+ fDateFormat = dtfmt.orphan();
+ if ( fDateFormat->getCalendar() ) {
+ fFromCalendar = fDateFormat->getCalendar()->clone();
+ fToCalendar = fDateFormat->getCalendar()->clone();
}
initializePattern(status);
}
-
-
DateIntervalFormat* U_EXPORT2
-DateIntervalFormat::create(DateFormat* dtfmt,
+DateIntervalFormat::create(const Locale& locale,
DateIntervalInfo* dtitvinf,
const UnicodeString* skeleton,
UErrorCode& status) {
- DateIntervalFormat* f = new DateIntervalFormat(dtfmt, dtitvinf,
+ DateIntervalFormat* f = new DateIntervalFormat(locale, dtitvinf,
skeleton, status);
if ( f == NULL ) {
status = U_MEMORY_ALLOCATION_ERROR;
- delete dtfmt;
delete dtitvinf;
} else if ( U_FAILURE(status) ) {
// safe to delete f, although nothing acutally is saved
* includes year, month, and date when year, month, and date differs.
*
* @param status output param set to success/failure code on exit
- * @draft ICU 4.0
+ * @stable ICU 4.0
*/
void
DateIntervalFormat::initializePattern(UErrorCode& status) {
return;
}
const Locale& locale = fDateFormat->getSmpFmtLocale();
- DateTimePatternGenerator* dtpng = DateTimePatternGenerator::createInstance(locale, status);
- if ( U_FAILURE(status) ) {
- delete dtpng;
- return;
- }
if ( fSkeleton.isEmpty() ) {
UnicodeString fullPattern;
fDateFormat->toPattern(fullPattern);
#endif
// fSkeleton is already set by createDateIntervalInstance()
// or by createInstance(UnicodeString skeleton, .... )
- fSkeleton = dtpng->getSkeleton(fullPattern, status);
+ fSkeleton = DateTimePatternGenerator::staticGetSkeleton(
+ fullPattern, status);
if ( U_FAILURE(status) ) {
- delete dtpng;
return;
}
}
/* the difference between time skeleton and normalizedTimeSkeleton are:
- * 1. both 'H' and 'h' are normalized as 'h' in normalized time skeleton,
+ * 1. (Formerly, normalized time skeleton folded 'H' to 'h'; no longer true)
* 2. 'a' is omitted in normalized time skeleton.
- * 3. there is only one appearance for 'h', 'm','v', 'z' in normalized
+ * 3. there is only one appearance for 'h' or 'H', 'm','v', 'z' in normalized
* time skeleton
*
* The difference between date skeleton and normalizedDateSkeleton are:
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
if ( timeSkeleton.length() != 0 ) {
if ( dateSkeleton.length() == 0 ) {
// prefix with yMd
- timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort]);
- UnicodeString pattern =dtpng->getBestPattern(timeSkeleton, status);
+ timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort], -1);
+ UnicodeString pattern = DateFormat::getBestPattern(
+ locale, timeSkeleton, status);
if ( U_FAILURE(status) ) {
- delete dtpng;
return;
}
// for fall back interval patterns,
} else {
// TODO: fall back
}
- delete dtpng;
return;
} // end of skeleton not found
// interval patterns for skeleton are found in resource
// done
} else if ( dateSkeleton.length() == 0 ) {
// prefix with yMd
- timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort]);
- UnicodeString pattern =dtpng->getBestPattern(timeSkeleton, status);
+ timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort], -1);
+ UnicodeString pattern = DateFormat::getBestPattern(
+ locale, timeSkeleton, status);
if ( U_FAILURE(status) ) {
- delete dtpng;
return;
}
// for fall back interval patterns,
if ( !fieldExistsInSkeleton(UCAL_DATE, dateSkeleton) ) {
// prefix skeleton with 'd'
skeleton.insert(0, LOW_D);
- setFallbackPattern(UCAL_DATE, skeleton, dtpng, status);
+ setFallbackPattern(UCAL_DATE, skeleton, status);
}
if ( !fieldExistsInSkeleton(UCAL_MONTH, dateSkeleton) ) {
// then prefix skeleton with 'M'
skeleton.insert(0, CAP_M);
- setFallbackPattern(UCAL_MONTH, skeleton, dtpng, status);
+ setFallbackPattern(UCAL_MONTH, skeleton, status);
}
if ( !fieldExistsInSkeleton(UCAL_YEAR, dateSkeleton) ) {
// then prefix skeleton with 'y'
skeleton.insert(0, LOW_Y);
- setFallbackPattern(UCAL_YEAR, skeleton, dtpng, status);
+ setFallbackPattern(UCAL_YEAR, skeleton, 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;
- delete dtpng;
- return;
- }
-
- if ( calData == NULL ) {
- status = U_MEMORY_ALLOCATION_ERROR;
- delete dtpng;
- 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) ) {
- delete dtpng;
+ if ( fDateTimeFormat == NULL ) {
+ // earlier failure getting dateTimeFormat
return;
}
- UnicodeString datePattern = dtpng->getBestPattern(dateSkeleton, status);
+ UnicodeString datePattern = DateFormat::getBestPattern(
+ locale, 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);
}
- delete dtpng;
}
int32_t MCount = 0;
int32_t yCount = 0;
int32_t hCount = 0;
+ int32_t HCount = 0;
int32_t mCount = 0;
int32_t vCount = 0;
int32_t zCount = 0;
case LOW_G:
case LOW_E:
case LOW_C:
+ case CAP_U:
+ case LOW_R:
normalizedDateSkeleton.append(ch);
dateSkeleton.append(ch);
break;
timeSkeleton.append(ch);
break;
case LOW_H:
- case CAP_H:
timeSkeleton.append(ch);
++hCount;
break;
+ case CAP_H:
+ timeSkeleton.append(ch);
+ ++HCount;
+ break;
case LOW_M:
timeSkeleton.append(ch);
++mCount;
++vCount;
timeSkeleton.append(ch);
break;
- // FIXME: what is the difference between CAP_V/Z and LOW_V/Z
case CAP_V:
case CAP_Z:
case LOW_K:
/* generate normalized form for date*/
if ( yCount != 0 ) {
- normalizedDateSkeleton.append(LOW_Y);
+ for (i = 0; i < yCount; ++i) {
+ normalizedDateSkeleton.append(LOW_Y);
+ }
}
if ( MCount != 0 ) {
if ( MCount < 3 ) {
}
/* generate normalized form for time */
- if ( hCount != 0 ) {
+ if ( HCount != 0 ) {
+ normalizedTimeSkeleton.append(CAP_H);
+ }
+ else if ( hCount != 0 ) {
normalizedTimeSkeleton.append(LOW_H);
}
if ( mCount != 0 ) {
* @return whether the resource is found for the skeleton.
* TRUE if interval pattern found for the skeleton,
* FALSE otherwise.
- * @draft ICU 4.0
+ * @stable ICU 4.0
*/
UBool
DateIntervalFormat::setSeparateDateTimePtn(
return false;
}
+ // Set patterns for fallback use, need to do this
+ // before returning if differenceInfo == -1
+ UErrorCode status;
+ if ( dateSkeleton.length() != 0) {
+ status = U_ZERO_ERROR;
+ fDatePattern = new UnicodeString(DateFormat::getBestPattern(
+ fLocale, dateSkeleton, status));
+ }
+ if ( timeSkeleton.length() != 0) {
+ status = U_ZERO_ERROR;
+ fTimePattern = new UnicodeString(DateFormat::getBestPattern(
+ fLocale, 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;
void
DateIntervalFormat::setFallbackPattern(UCalendarDateFields field,
const UnicodeString& skeleton,
- DateTimePatternGenerator* dtpng,
UErrorCode& status) {
if ( U_FAILURE(status) ) {
return;
}
- UnicodeString pattern =dtpng->getBestPattern(skeleton, status);
+ UnicodeString pattern = DateFormat::getBestPattern(
+ fLocale, skeleton, status);
if ( U_FAILURE(status) ) {
return;
}
const UnicodeString* pattern = &intervalPattern;
UBool order = laterDateFirst;
// check for "latestFirst:" or "earliestFirst:" prefix
- int8_t prefixLength = sizeof(gLaterFirstPrefix)/sizeof(gLaterFirstPrefix[0]);
- int8_t earliestFirstLength = sizeof(gEarlierFirstPrefix)/sizeof(gEarlierFirstPrefix[0]);
+ int8_t prefixLength = UPRV_LENGTHOF(gLaterFirstPrefix);
+ int8_t earliestFirstLength = UPRV_LENGTHOF(gEarlierFirstPrefix);
UnicodeString realPattern;
if ( intervalPattern.startsWith(gLaterFirstPrefix, prefixLength) ) {
order = true;
* through extending skeleton or not.
* TRUE if interval pattern is found by
* extending skeleton, FALSE otherwise.
- * @draft ICU 4.0
+ * @stable ICU 4.0
*/
UBool
DateIntervalFormat::setIntervalPattern(UCalendarDateFields field,
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;
+ }
+ int32_t 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);
+ }
+ }
+}
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
- UnicodeString* earlierDate = new UnicodeString();
- *earlierDate = fDateFormat->format(fromCalendar, *earlierDate, pos);
- UnicodeString* laterDate = new UnicodeString();
- *laterDate = fDateFormat->format(toCalendar, *laterDate, pos);
+ if (formatDatePlusTimeRange) {
+ fDateFormat->toPattern(fullPattern); // save current pattern, restore later
+ fDateFormat->applyPattern(*fTimePattern);
+ }
+ FieldPosition otherPos;
+ otherPos.setField(pos.getField());
+ UnicodeString earlierDate;
+ fDateFormat->format(fromCalendar, earlierDate, pos);
+ UnicodeString laterDate;
+ fDateFormat->format(toCalendar, laterDate, otherPos);
UnicodeString fallbackPattern;
fInfo->getFallbackIntervalPattern(fallbackPattern);
- Formattable fmtArray[2];
- fmtArray[0].adoptString(earlierDate);
- fmtArray[1].adoptString(laterDate);
-
- UnicodeString fallback;
- MessageFormat::format(fallbackPattern, fmtArray, 2, fallback, status);
+ adjustPosition(fallbackPattern, earlierDate, pos, laterDate, otherPos, pos);
+ UnicodeString fallbackRange;
+ SimpleFormatter(fallbackPattern, 2, 2, status).
+ format(earlierDate, laterDate, fallbackRange, status);
+ if ( U_SUCCESS(status) && formatDatePlusTimeRange ) {
+ // fallbackRange has just the time range, need to format the date part and combine that
+ UnicodeString dateTimeFormatNoQuote(*fDateTimeFormat);
+ dateTimeFormatNoQuote.findAndReplace(UnicodeString(0x0027), UnicodeString());
+ fDateFormat->applyPattern(*fDatePattern);
+ UnicodeString datePortion;
+ otherPos.setBeginIndex(0);
+ otherPos.setEndIndex(0);
+ fDateFormat->format(fromCalendar, datePortion, otherPos);
+ adjustPosition(dateTimeFormatNoQuote, fallbackRange, pos, datePortion, otherPos, pos);
+ const UnicodeString *values[2] = {
+ &fallbackRange, // {0} is time range
+ &datePortion, // {1} is single date portion
+ };
+ SimpleFormatter(dateTimeFormatNoQuote, 2, 2, status).
+ formatAndReplace(values, 2, fallbackRange, NULL, 0, status);
+ }
if ( U_SUCCESS(status) ) {
- appendTo.append(fallback);
+ appendTo.append(fallbackRange);
+ }
+ if (formatDatePlusTimeRange) {
+ // restore full pattern
+ fDateFormat->applyPattern(fullPattern);
}
return appendTo;
}
DateIntervalInfo::parseSkeleton(inputSkeleton, inputSkeletonFieldWidth);
DateIntervalInfo::parseSkeleton(bestMatchSkeleton, bestMatchSkeletonFieldWidth);
if ( differenceInfo == 2 ) {
- adjustedPtn.findAndReplace("v", "z");
+ adjustedPtn.findAndReplace(UnicodeString((UChar)0x76 /* v */),
+ UnicodeString((UChar)0x7a /* z */));
}
UBool inQuote = false;
void
-DateIntervalFormat::concatSingleDate2TimeInterval(const UChar* format,
- int32_t formatLen,
+DateIntervalFormat::concatSingleDate2TimeInterval(UnicodeString& format,
const UnicodeString& datePattern,
UCalendarDateFields field,
UErrorCode& status) {
}
PatternInfo& timeItvPtnInfo = fIntervalPatterns[itvPtnIndex];
if ( !timeItvPtnInfo.firstPart.isEmpty() ) {
- // UnicodeString allocated here is adopted, so no need to delete
- UnicodeString* timeIntervalPattern = new UnicodeString(timeItvPtnInfo.firstPart);
- timeIntervalPattern->append(timeItvPtnInfo.secondPart);
- UnicodeString* dateStr = new UnicodeString(datePattern);
- Formattable fmtArray[2];
- fmtArray[0].adoptString(timeIntervalPattern);
- fmtArray[1].adoptString(dateStr);
+ UnicodeString timeIntervalPattern(timeItvPtnInfo.firstPart);
+ timeIntervalPattern.append(timeItvPtnInfo.secondPart);
UnicodeString combinedPattern;
- MessageFormat::format(UnicodeString(TRUE, format, formatLen),
- fmtArray, 2, combinedPattern, status);
+ SimpleFormatter(format, 2, 2, status).
+ format(timeIntervalPattern, datePattern, combinedPattern, status);
if ( U_FAILURE(status) ) {
return;
}
/*wWd*/ LOW_W, CAP_W, LOW_D,
/*DEF*/ CAP_D, CAP_E, CAP_F,
/*ahH*/ LOW_A, LOW_H, CAP_H,
- /*m..*/ LOW_M,
+ /*msS*/ LOW_M, LOW_S, CAP_S, // MINUTE, SECOND, MILLISECOND
+ /*z.Y*/ LOW_Z, SPACE, CAP_Y, // ZONE_OFFSET, DST_OFFSET, YEAR_WOY,
+ /*eug*/ LOW_E, LOW_U, LOW_G, // DOW_LOCAL, EXTENDED_YEAR, JULIAN_DAY,
+ /*A..*/ CAP_A, SPACE, SPACE, // MILLISECONDS_IN_DAY, IS_LEAP_MONTH, FIELD_COUNT
};