michael@0: /******************************************************************************* michael@0: * Copyright (C) 2008-2013, International Business Machines Corporation and michael@0: * others. All Rights Reserved. michael@0: ******************************************************************************* michael@0: * michael@0: * File DTITVFMT.CPP michael@0: * michael@0: ******************************************************************************* michael@0: */ michael@0: michael@0: #include "utypeinfo.h" // for 'typeid' to work michael@0: michael@0: #include "unicode/dtitvfmt.h" michael@0: michael@0: #if !UCONFIG_NO_FORMATTING michael@0: michael@0: //TODO: put in compilation michael@0: //#define DTITVFMT_DEBUG 1 michael@0: michael@0: #include "cstring.h" michael@0: #include "unicode/msgfmt.h" michael@0: #include "unicode/dtptngen.h" michael@0: #include "unicode/dtitvinf.h" michael@0: #include "unicode/calendar.h" michael@0: #include "dtitv_impl.h" michael@0: michael@0: #ifdef DTITVFMT_DEBUG michael@0: #include michael@0: #include "cstring.h" michael@0: #endif michael@0: michael@0: #include "gregoimp.h" michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: michael@0: michael@0: #ifdef DTITVFMT_DEBUG michael@0: #define PRINTMESG(msg) { std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; } michael@0: #endif michael@0: michael@0: michael@0: static const UChar gDateFormatSkeleton[][11] = { michael@0: //yMMMMEEEEd michael@0: {LOW_Y, CAP_M, CAP_M, CAP_M, CAP_M, CAP_E, CAP_E, CAP_E, CAP_E, LOW_D, 0}, michael@0: //yMMMMd michael@0: {LOW_Y, CAP_M, CAP_M, CAP_M, CAP_M, LOW_D, 0}, michael@0: //yMMMd michael@0: {LOW_Y, CAP_M, CAP_M, CAP_M, LOW_D, 0}, michael@0: //yMd michael@0: {LOW_Y, CAP_M, LOW_D, 0} }; michael@0: michael@0: michael@0: static const char gDateTimePatternsTag[]="DateTimePatterns"; michael@0: michael@0: michael@0: // latestFirst: michael@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}; michael@0: michael@0: // earliestFirst: michael@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}; michael@0: michael@0: michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalFormat) michael@0: michael@0: michael@0: michael@0: DateIntervalFormat* U_EXPORT2 michael@0: DateIntervalFormat::createInstance(const UnicodeString& skeleton, michael@0: UErrorCode& status) { michael@0: return createInstance(skeleton, Locale::getDefault(), status); michael@0: } michael@0: michael@0: michael@0: DateIntervalFormat* U_EXPORT2 michael@0: DateIntervalFormat::createInstance(const UnicodeString& skeleton, michael@0: const Locale& locale, michael@0: UErrorCode& status) { michael@0: #ifdef DTITVFMT_DEBUG michael@0: char result[1000]; michael@0: char result_1[1000]; michael@0: char mesg[2000]; michael@0: skeleton.extract(0, skeleton.length(), result, "UTF-8"); michael@0: UnicodeString pat; michael@0: ((SimpleDateFormat*)dtfmt)->toPattern(pat); michael@0: pat.extract(0, pat.length(), result_1, "UTF-8"); michael@0: sprintf(mesg, "skeleton: %s; pattern: %s\n", result, result_1); michael@0: PRINTMESG(mesg) michael@0: #endif michael@0: michael@0: DateIntervalInfo* dtitvinf = new DateIntervalInfo(locale, status); michael@0: return create(locale, dtitvinf, &skeleton, status); michael@0: } michael@0: michael@0: michael@0: michael@0: DateIntervalFormat* U_EXPORT2 michael@0: DateIntervalFormat::createInstance(const UnicodeString& skeleton, michael@0: const DateIntervalInfo& dtitvinf, michael@0: UErrorCode& status) { michael@0: return createInstance(skeleton, Locale::getDefault(), dtitvinf, status); michael@0: } michael@0: michael@0: michael@0: DateIntervalFormat* U_EXPORT2 michael@0: DateIntervalFormat::createInstance(const UnicodeString& skeleton, michael@0: const Locale& locale, michael@0: const DateIntervalInfo& dtitvinf, michael@0: UErrorCode& status) { michael@0: DateIntervalInfo* ptn = dtitvinf.clone(); michael@0: return create(locale, ptn, &skeleton, status); michael@0: } michael@0: michael@0: michael@0: DateIntervalFormat::DateIntervalFormat() michael@0: : fInfo(NULL), michael@0: fDateFormat(NULL), michael@0: fFromCalendar(NULL), michael@0: fToCalendar(NULL), michael@0: fDtpng(NULL) michael@0: {} michael@0: michael@0: michael@0: DateIntervalFormat::DateIntervalFormat(const DateIntervalFormat& itvfmt) michael@0: : Format(itvfmt), michael@0: fInfo(NULL), michael@0: fDateFormat(NULL), michael@0: fFromCalendar(NULL), michael@0: fToCalendar(NULL), michael@0: fDtpng(NULL) { michael@0: *this = itvfmt; michael@0: } michael@0: michael@0: michael@0: DateIntervalFormat& michael@0: DateIntervalFormat::operator=(const DateIntervalFormat& itvfmt) { michael@0: if ( this != &itvfmt ) { michael@0: delete fDateFormat; michael@0: delete fInfo; michael@0: delete fFromCalendar; michael@0: delete fToCalendar; michael@0: delete fDtpng; michael@0: if ( itvfmt.fDateFormat ) { michael@0: fDateFormat = (SimpleDateFormat*)itvfmt.fDateFormat->clone(); michael@0: } else { michael@0: fDateFormat = NULL; michael@0: } michael@0: if ( itvfmt.fInfo ) { michael@0: fInfo = itvfmt.fInfo->clone(); michael@0: } else { michael@0: fInfo = NULL; michael@0: } michael@0: if ( itvfmt.fFromCalendar ) { michael@0: fFromCalendar = itvfmt.fFromCalendar->clone(); michael@0: } else { michael@0: fFromCalendar = NULL; michael@0: } michael@0: if ( itvfmt.fToCalendar ) { michael@0: fToCalendar = itvfmt.fToCalendar->clone(); michael@0: } else { michael@0: fToCalendar = NULL; michael@0: } michael@0: fSkeleton = itvfmt.fSkeleton; michael@0: int8_t i; michael@0: for ( i = 0; i< DateIntervalInfo::kIPI_MAX_INDEX; ++i ) { michael@0: fIntervalPatterns[i] = itvfmt.fIntervalPatterns[i]; michael@0: } michael@0: if (itvfmt.fDtpng) { michael@0: fDtpng = itvfmt.fDtpng->clone(); michael@0: } michael@0: } michael@0: return *this; michael@0: } michael@0: michael@0: michael@0: DateIntervalFormat::~DateIntervalFormat() { michael@0: delete fInfo; michael@0: delete fDateFormat; michael@0: delete fFromCalendar; michael@0: delete fToCalendar; michael@0: delete fDtpng; michael@0: } michael@0: michael@0: michael@0: Format* michael@0: DateIntervalFormat::clone(void) const { michael@0: return new DateIntervalFormat(*this); michael@0: } michael@0: michael@0: michael@0: UBool michael@0: DateIntervalFormat::operator==(const Format& other) const { michael@0: if (typeid(*this) == typeid(other)) { michael@0: const DateIntervalFormat* fmt = (DateIntervalFormat*)&other; michael@0: #ifdef DTITVFMT_DEBUG michael@0: UBool equal; michael@0: equal = (this == fmt); michael@0: michael@0: equal = (*fInfo == *fmt->fInfo); michael@0: equal = (*fDateFormat == *fmt->fDateFormat); michael@0: equal = fFromCalendar->isEquivalentTo(*fmt->fFromCalendar) ; michael@0: equal = fToCalendar->isEquivalentTo(*fmt->fToCalendar) ; michael@0: equal = (fSkeleton == fmt->fSkeleton); michael@0: #endif michael@0: UBool res; michael@0: res = ( this == fmt ) || michael@0: ( Format::operator==(other) && michael@0: fInfo && michael@0: ( *fInfo == *fmt->fInfo ) && michael@0: fDateFormat && michael@0: ( *fDateFormat == *fmt->fDateFormat ) && michael@0: fFromCalendar && michael@0: fFromCalendar->isEquivalentTo(*fmt->fFromCalendar) && michael@0: fToCalendar && michael@0: fToCalendar->isEquivalentTo(*fmt->fToCalendar) && michael@0: fSkeleton == fmt->fSkeleton && michael@0: fDtpng && michael@0: (*fDtpng == *fmt->fDtpng) ); michael@0: int8_t i; michael@0: for (i = 0; i< DateIntervalInfo::kIPI_MAX_INDEX && res == TRUE; ++i ) { michael@0: res = ( fIntervalPatterns[i].firstPart == michael@0: fmt->fIntervalPatterns[i].firstPart) && michael@0: ( fIntervalPatterns[i].secondPart == michael@0: fmt->fIntervalPatterns[i].secondPart ) && michael@0: ( fIntervalPatterns[i].laterDateFirst == michael@0: fmt->fIntervalPatterns[i].laterDateFirst) ; michael@0: } michael@0: return res; michael@0: } michael@0: return FALSE; michael@0: } michael@0: michael@0: michael@0: michael@0: UnicodeString& michael@0: DateIntervalFormat::format(const Formattable& obj, michael@0: UnicodeString& appendTo, michael@0: FieldPosition& fieldPosition, michael@0: UErrorCode& status) const { michael@0: if ( U_FAILURE(status) ) { michael@0: return appendTo; michael@0: } michael@0: michael@0: if ( obj.getType() == Formattable::kObject ) { michael@0: const UObject* formatObj = obj.getObject(); michael@0: const DateInterval* interval = dynamic_cast(formatObj); michael@0: if (interval != NULL){ michael@0: return format(interval, appendTo, fieldPosition, status); michael@0: } michael@0: } michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return appendTo; michael@0: } michael@0: michael@0: michael@0: UnicodeString& michael@0: DateIntervalFormat::format(const DateInterval* dtInterval, michael@0: UnicodeString& appendTo, michael@0: FieldPosition& fieldPosition, michael@0: UErrorCode& status) const { michael@0: if ( U_FAILURE(status) ) { michael@0: return appendTo; michael@0: } michael@0: michael@0: if ( fFromCalendar != NULL && fToCalendar != NULL && michael@0: fDateFormat != NULL && fInfo != NULL ) { michael@0: fFromCalendar->setTime(dtInterval->getFromDate(), status); michael@0: fToCalendar->setTime(dtInterval->getToDate(), status); michael@0: if ( U_SUCCESS(status) ) { michael@0: return format(*fFromCalendar, *fToCalendar, appendTo,fieldPosition, status); michael@0: } michael@0: } michael@0: return appendTo; michael@0: } michael@0: michael@0: michael@0: UnicodeString& michael@0: DateIntervalFormat::format(Calendar& fromCalendar, michael@0: Calendar& toCalendar, michael@0: UnicodeString& appendTo, michael@0: FieldPosition& pos, michael@0: UErrorCode& status) const { michael@0: if ( U_FAILURE(status) ) { michael@0: return appendTo; michael@0: } michael@0: michael@0: // not support different calendar types and time zones michael@0: //if ( fromCalendar.getType() != toCalendar.getType() ) { michael@0: if ( !fromCalendar.isEquivalentTo(toCalendar) ) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return appendTo; michael@0: } michael@0: michael@0: // First, find the largest different calendar field. michael@0: UCalendarDateFields field = UCAL_FIELD_COUNT; michael@0: michael@0: if ( fromCalendar.get(UCAL_ERA,status) != toCalendar.get(UCAL_ERA,status)) { michael@0: field = UCAL_ERA; michael@0: } else if ( fromCalendar.get(UCAL_YEAR, status) != michael@0: toCalendar.get(UCAL_YEAR, status) ) { michael@0: field = UCAL_YEAR; michael@0: } else if ( fromCalendar.get(UCAL_MONTH, status) != michael@0: toCalendar.get(UCAL_MONTH, status) ) { michael@0: field = UCAL_MONTH; michael@0: } else if ( fromCalendar.get(UCAL_DATE, status) != michael@0: toCalendar.get(UCAL_DATE, status) ) { michael@0: field = UCAL_DATE; michael@0: } else if ( fromCalendar.get(UCAL_AM_PM, status) != michael@0: toCalendar.get(UCAL_AM_PM, status) ) { michael@0: field = UCAL_AM_PM; michael@0: } else if ( fromCalendar.get(UCAL_HOUR, status) != michael@0: toCalendar.get(UCAL_HOUR, status) ) { michael@0: field = UCAL_HOUR; michael@0: } else if ( fromCalendar.get(UCAL_MINUTE, status) != michael@0: toCalendar.get(UCAL_MINUTE, status) ) { michael@0: field = UCAL_MINUTE; michael@0: } michael@0: michael@0: if ( U_FAILURE(status) ) { michael@0: return appendTo; michael@0: } michael@0: if ( field == UCAL_FIELD_COUNT ) { michael@0: /* ignore the second/millisecond etc. small fields' difference. michael@0: * use single date when all the above are the same. michael@0: */ michael@0: return fDateFormat->format(fromCalendar, appendTo, pos); michael@0: } michael@0: michael@0: // following call should not set wrong status, michael@0: // all the pass-in fields are valid till here michael@0: int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field, michael@0: status); michael@0: const PatternInfo& intervalPattern = fIntervalPatterns[itvPtnIndex]; michael@0: michael@0: if ( intervalPattern.firstPart.isEmpty() && michael@0: intervalPattern.secondPart.isEmpty() ) { michael@0: if ( fDateFormat->isFieldUnitIgnored(field) ) { michael@0: /* the largest different calendar field is small than michael@0: * the smallest calendar field in pattern, michael@0: * return single date format. michael@0: */ michael@0: return fDateFormat->format(fromCalendar, appendTo, pos); michael@0: } michael@0: return fallbackFormat(fromCalendar, toCalendar, appendTo, pos, status); michael@0: } michael@0: // If the first part in interval pattern is empty, michael@0: // the 2nd part of it saves the full-pattern used in fall-back. michael@0: // For a 'real' interval pattern, the first part will never be empty. michael@0: if ( intervalPattern.firstPart.isEmpty() ) { michael@0: // fall back michael@0: UnicodeString originalPattern; michael@0: fDateFormat->toPattern(originalPattern); michael@0: fDateFormat->applyPattern(intervalPattern.secondPart); michael@0: appendTo = fallbackFormat(fromCalendar, toCalendar, appendTo, pos, status); michael@0: fDateFormat->applyPattern(originalPattern); michael@0: return appendTo; michael@0: } michael@0: Calendar* firstCal; michael@0: Calendar* secondCal; michael@0: if ( intervalPattern.laterDateFirst ) { michael@0: firstCal = &toCalendar; michael@0: secondCal = &fromCalendar; michael@0: } else { michael@0: firstCal = &fromCalendar; michael@0: secondCal = &toCalendar; michael@0: } michael@0: // break the interval pattern into 2 parts, michael@0: // first part should not be empty, michael@0: UnicodeString originalPattern; michael@0: fDateFormat->toPattern(originalPattern); michael@0: fDateFormat->applyPattern(intervalPattern.firstPart); michael@0: fDateFormat->format(*firstCal, appendTo, pos); michael@0: if ( !intervalPattern.secondPart.isEmpty() ) { michael@0: fDateFormat->applyPattern(intervalPattern.secondPart); michael@0: fDateFormat->format(*secondCal, appendTo, pos); michael@0: } michael@0: fDateFormat->applyPattern(originalPattern); michael@0: return appendTo; michael@0: } michael@0: michael@0: michael@0: michael@0: void michael@0: DateIntervalFormat::parseObject(const UnicodeString& /* source */, michael@0: Formattable& /* result */, michael@0: ParsePosition& /* parse_pos */) const { michael@0: // parseObject(const UnicodeString&, Formattable&, UErrorCode&) const michael@0: // will set status as U_INVALID_FORMAT_ERROR if michael@0: // parse_pos is still 0 michael@0: } michael@0: michael@0: michael@0: michael@0: michael@0: const DateIntervalInfo* michael@0: DateIntervalFormat::getDateIntervalInfo() const { michael@0: return fInfo; michael@0: } michael@0: michael@0: michael@0: void michael@0: DateIntervalFormat::setDateIntervalInfo(const DateIntervalInfo& newItvPattern, michael@0: UErrorCode& status) { michael@0: delete fInfo; michael@0: fInfo = new DateIntervalInfo(newItvPattern); michael@0: if ( fDateFormat ) { michael@0: initializePattern(status); michael@0: } michael@0: } michael@0: michael@0: michael@0: michael@0: const DateFormat* michael@0: DateIntervalFormat::getDateFormat() const { michael@0: return fDateFormat; michael@0: } michael@0: michael@0: michael@0: void michael@0: DateIntervalFormat::adoptTimeZone(TimeZone* zone) michael@0: { michael@0: if (fDateFormat != NULL) { michael@0: fDateFormat->adoptTimeZone(zone); michael@0: } michael@0: // The fDateFormat has the master calendar for the DateIntervalFormat and has michael@0: // ownership of any adopted TimeZone; fFromCalendar and fToCalendar are internal michael@0: // work clones of that calendar (and should not also be given ownership of the michael@0: // adopted TimeZone). michael@0: if (fFromCalendar) { michael@0: fFromCalendar->setTimeZone(*zone); michael@0: } michael@0: if (fToCalendar) { michael@0: fToCalendar->setTimeZone(*zone); michael@0: } michael@0: } michael@0: michael@0: void michael@0: DateIntervalFormat::setTimeZone(const TimeZone& zone) michael@0: { michael@0: if (fDateFormat != NULL) { michael@0: fDateFormat->setTimeZone(zone); michael@0: } michael@0: // The fDateFormat has the master calendar for the DateIntervalFormat; michael@0: // fFromCalendar and fToCalendar are internal work clones of that calendar. michael@0: if (fFromCalendar) { michael@0: fFromCalendar->setTimeZone(zone); michael@0: } michael@0: if (fToCalendar) { michael@0: fToCalendar->setTimeZone(zone); michael@0: } michael@0: } michael@0: michael@0: const TimeZone& michael@0: DateIntervalFormat::getTimeZone() const michael@0: { michael@0: if (fDateFormat != NULL) { michael@0: return fDateFormat->getTimeZone(); michael@0: } michael@0: // If fDateFormat is NULL (unexpected), create default timezone. michael@0: return *(TimeZone::createDefault()); michael@0: } michael@0: michael@0: DateIntervalFormat::DateIntervalFormat(const Locale& locale, michael@0: DateIntervalInfo* dtItvInfo, michael@0: const UnicodeString* skeleton, michael@0: UErrorCode& status) michael@0: : fInfo(NULL), michael@0: fDateFormat(NULL), michael@0: fFromCalendar(NULL), michael@0: fToCalendar(NULL), michael@0: fDtpng(NULL) michael@0: { michael@0: if ( U_FAILURE(status) ) { michael@0: delete dtItvInfo; michael@0: return; michael@0: } michael@0: fDtpng = DateTimePatternGenerator::createInstance(locale, status); michael@0: SimpleDateFormat* dtfmt = createSDFPatternInstance(*skeleton, locale, michael@0: fDtpng, status); michael@0: if ( U_FAILURE(status) ) { michael@0: delete dtItvInfo; michael@0: delete fDtpng; michael@0: delete dtfmt; michael@0: return; michael@0: } michael@0: if ( dtfmt == NULL || dtItvInfo == NULL || fDtpng == NULL ) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: // safe to delete NULL michael@0: delete dtfmt; michael@0: delete dtItvInfo; michael@0: delete fDtpng; michael@0: return; michael@0: } michael@0: if ( skeleton ) { michael@0: fSkeleton = *skeleton; michael@0: } michael@0: fInfo = dtItvInfo; michael@0: fDateFormat = dtfmt; michael@0: if ( dtfmt->getCalendar() ) { michael@0: fFromCalendar = dtfmt->getCalendar()->clone(); michael@0: fToCalendar = dtfmt->getCalendar()->clone(); michael@0: } else { michael@0: fFromCalendar = NULL; michael@0: fToCalendar = NULL; michael@0: } michael@0: initializePattern(status); michael@0: } michael@0: michael@0: michael@0: SimpleDateFormat* U_EXPORT2 michael@0: DateIntervalFormat::createSDFPatternInstance(const UnicodeString& skeleton, michael@0: const Locale& locale, michael@0: DateTimePatternGenerator* dtpng, michael@0: UErrorCode& status) michael@0: { michael@0: if ( U_FAILURE(status) ) { michael@0: return NULL; michael@0: } michael@0: michael@0: const UnicodeString pattern = dtpng->getBestPattern(skeleton, status); michael@0: if ( U_FAILURE(status) ) { michael@0: return NULL; michael@0: } michael@0: SimpleDateFormat* dtfmt = new SimpleDateFormat(pattern, locale, status); michael@0: if ( U_FAILURE(status) ) { michael@0: delete dtfmt; michael@0: return NULL; michael@0: } michael@0: return dtfmt; michael@0: } michael@0: michael@0: michael@0: DateIntervalFormat* U_EXPORT2 michael@0: DateIntervalFormat::create(const Locale& locale, michael@0: DateIntervalInfo* dtitvinf, michael@0: const UnicodeString* skeleton, michael@0: UErrorCode& status) { michael@0: DateIntervalFormat* f = new DateIntervalFormat(locale, dtitvinf, michael@0: skeleton, status); michael@0: if ( f == NULL ) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: delete dtitvinf; michael@0: } else if ( U_FAILURE(status) ) { michael@0: // safe to delete f, although nothing acutally is saved michael@0: delete f; michael@0: f = 0; michael@0: } michael@0: return f; michael@0: } michael@0: michael@0: michael@0: michael@0: /** michael@0: * Initialize interval patterns locale to this formatter michael@0: * michael@0: * This code is a bit complicated since michael@0: * 1. the interval patterns saved in resource bundle files are interval michael@0: * patterns based on date or time only. michael@0: * It does not have interval patterns based on both date and time. michael@0: * Interval patterns on both date and time are algorithm generated. michael@0: * michael@0: * For example, it has interval patterns on skeleton "dMy" and "hm", michael@0: * but it does not have interval patterns on skeleton "dMyhm". michael@0: * michael@0: * The rule to genearte interval patterns for both date and time skeleton are michael@0: * 1) when the year, month, or day differs, concatenate the two original michael@0: * expressions with a separator between, michael@0: * For example, interval pattern from "Jan 10, 2007 10:10 am" michael@0: * to "Jan 11, 2007 10:10am" is michael@0: * "Jan 10, 2007 10:10 am - Jan 11, 2007 10:10am" michael@0: * michael@0: * 2) otherwise, present the date followed by the range expression michael@0: * for the time. michael@0: * For example, interval pattern from "Jan 10, 2007 10:10 am" michael@0: * to "Jan 10, 2007 11:10am" is michael@0: * "Jan 10, 2007 10:10 am - 11:10am" michael@0: * michael@0: * 2. even a pattern does not request a certion calendar field, michael@0: * the interval pattern needs to include such field if such fields are michael@0: * different between 2 dates. michael@0: * For example, a pattern/skeleton is "hm", but the interval pattern michael@0: * includes year, month, and date when year, month, and date differs. michael@0: * michael@0: * @param status output param set to success/failure code on exit michael@0: * @stable ICU 4.0 michael@0: */ michael@0: void michael@0: DateIntervalFormat::initializePattern(UErrorCode& status) { michael@0: if ( U_FAILURE(status) ) { michael@0: return; michael@0: } michael@0: const Locale& locale = fDateFormat->getSmpFmtLocale(); michael@0: if ( fSkeleton.isEmpty() ) { michael@0: UnicodeString fullPattern; michael@0: fDateFormat->toPattern(fullPattern); michael@0: #ifdef DTITVFMT_DEBUG michael@0: char result[1000]; michael@0: char result_1[1000]; michael@0: char mesg[2000]; michael@0: fSkeleton.extract(0, fSkeleton.length(), result, "UTF-8"); michael@0: sprintf(mesg, "in getBestSkeleton: fSkeleton: %s; \n", result); michael@0: PRINTMESG(mesg) michael@0: #endif michael@0: // fSkeleton is already set by createDateIntervalInstance() michael@0: // or by createInstance(UnicodeString skeleton, .... ) michael@0: fSkeleton = fDtpng->getSkeleton(fullPattern, status); michael@0: if ( U_FAILURE(status) ) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: // initialize the fIntervalPattern ordering michael@0: int8_t i; michael@0: for ( i = 0; i < DateIntervalInfo::kIPI_MAX_INDEX; ++i ) { michael@0: fIntervalPatterns[i].laterDateFirst = fInfo->getDefaultOrder(); michael@0: } michael@0: michael@0: /* Check whether the skeleton is a combination of date and time. michael@0: * For the complication reason 1 explained above. michael@0: */ michael@0: UnicodeString dateSkeleton; michael@0: UnicodeString timeSkeleton; michael@0: UnicodeString normalizedTimeSkeleton; michael@0: UnicodeString normalizedDateSkeleton; michael@0: michael@0: michael@0: /* the difference between time skeleton and normalizedTimeSkeleton are: michael@0: * 1. (Formerly, normalized time skeleton folded 'H' to 'h'; no longer true) michael@0: * 2. 'a' is omitted in normalized time skeleton. michael@0: * 3. there is only one appearance for 'h' or 'H', 'm','v', 'z' in normalized michael@0: * time skeleton michael@0: * michael@0: * The difference between date skeleton and normalizedDateSkeleton are: michael@0: * 1. both 'y' and 'd' appear only once in normalizeDateSkeleton michael@0: * 2. 'E' and 'EE' are normalized into 'EEE' michael@0: * 3. 'MM' is normalized into 'M' michael@0: */ michael@0: getDateTimeSkeleton(fSkeleton, dateSkeleton, normalizedDateSkeleton, michael@0: timeSkeleton, normalizedTimeSkeleton); michael@0: michael@0: #ifdef DTITVFMT_DEBUG michael@0: char result[1000]; michael@0: char result_1[1000]; michael@0: char mesg[2000]; michael@0: fSkeleton.extract(0, fSkeleton.length(), result, "UTF-8"); michael@0: sprintf(mesg, "in getBestSkeleton: fSkeleton: %s; \n", result); michael@0: PRINTMESG(mesg) michael@0: #endif michael@0: michael@0: michael@0: UBool found = setSeparateDateTimePtn(normalizedDateSkeleton, michael@0: normalizedTimeSkeleton); michael@0: michael@0: if ( found == false ) { michael@0: // use fallback michael@0: // TODO: if user asks "m"(minute), but "d"(day) differ michael@0: if ( timeSkeleton.length() != 0 ) { michael@0: if ( dateSkeleton.length() == 0 ) { michael@0: // prefix with yMd michael@0: timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort], -1); michael@0: UnicodeString pattern = fDtpng->getBestPattern(timeSkeleton, status); michael@0: if ( U_FAILURE(status) ) { michael@0: return; michael@0: } michael@0: // for fall back interval patterns, michael@0: // the first part of the pattern is empty, michael@0: // the second part of the pattern is the full-pattern michael@0: // should be used in fall-back. michael@0: setPatternInfo(UCAL_DATE, NULL, &pattern, fInfo->getDefaultOrder()); michael@0: setPatternInfo(UCAL_MONTH, NULL, &pattern, fInfo->getDefaultOrder()); michael@0: setPatternInfo(UCAL_YEAR, NULL, &pattern, fInfo->getDefaultOrder()); michael@0: } else { michael@0: // TODO: fall back michael@0: } michael@0: } else { michael@0: // TODO: fall back michael@0: } michael@0: return; michael@0: } // end of skeleton not found michael@0: // interval patterns for skeleton are found in resource michael@0: if ( timeSkeleton.length() == 0 ) { michael@0: // done michael@0: } else if ( dateSkeleton.length() == 0 ) { michael@0: // prefix with yMd michael@0: timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort], -1); michael@0: UnicodeString pattern = fDtpng->getBestPattern(timeSkeleton, status); michael@0: if ( U_FAILURE(status) ) { michael@0: return; michael@0: } michael@0: // for fall back interval patterns, michael@0: // the first part of the pattern is empty, michael@0: // the second part of the pattern is the full-pattern michael@0: // should be used in fall-back. michael@0: setPatternInfo(UCAL_DATE, NULL, &pattern, fInfo->getDefaultOrder()); michael@0: setPatternInfo(UCAL_MONTH, NULL, &pattern, fInfo->getDefaultOrder()); michael@0: setPatternInfo(UCAL_YEAR, NULL, &pattern, fInfo->getDefaultOrder()); michael@0: } else { michael@0: /* if both present, michael@0: * 1) when the year, month, or day differs, michael@0: * concatenate the two original expressions with a separator between, michael@0: * 2) otherwise, present the date followed by the michael@0: * range expression for the time. michael@0: */ michael@0: /* michael@0: * 1) when the year, month, or day differs, michael@0: * concatenate the two original expressions with a separator between, michael@0: */ michael@0: // if field exists, use fall back michael@0: UnicodeString skeleton = fSkeleton; michael@0: if ( !fieldExistsInSkeleton(UCAL_DATE, dateSkeleton) ) { michael@0: // prefix skeleton with 'd' michael@0: skeleton.insert(0, LOW_D); michael@0: setFallbackPattern(UCAL_DATE, skeleton, status); michael@0: } michael@0: if ( !fieldExistsInSkeleton(UCAL_MONTH, dateSkeleton) ) { michael@0: // then prefix skeleton with 'M' michael@0: skeleton.insert(0, CAP_M); michael@0: setFallbackPattern(UCAL_MONTH, skeleton, status); michael@0: } michael@0: if ( !fieldExistsInSkeleton(UCAL_YEAR, dateSkeleton) ) { michael@0: // then prefix skeleton with 'y' michael@0: skeleton.insert(0, LOW_Y); michael@0: setFallbackPattern(UCAL_YEAR, skeleton, status); michael@0: } michael@0: michael@0: /* michael@0: * 2) otherwise, present the date followed by the michael@0: * range expression for the time. michael@0: */ michael@0: // Need the Date/Time pattern for concatnation the date with michael@0: // the time interval. michael@0: // The date/time pattern ( such as {0} {1} ) is saved in michael@0: // calendar, that is why need to get the CalendarData here. michael@0: CalendarData* calData = new CalendarData(locale, NULL, status); michael@0: michael@0: if ( U_FAILURE(status) ) { michael@0: delete calData; michael@0: return; michael@0: } michael@0: michael@0: if ( calData == NULL ) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return; michael@0: } michael@0: michael@0: const UResourceBundle* dateTimePatternsRes = calData->getByKey( michael@0: gDateTimePatternsTag, status); michael@0: int32_t dateTimeFormatLength; michael@0: const UChar* dateTimeFormat = ures_getStringByIndex( michael@0: dateTimePatternsRes, michael@0: (int32_t)DateFormat::kDateTime, michael@0: &dateTimeFormatLength, &status); michael@0: if ( U_FAILURE(status) ) { michael@0: return; michael@0: } michael@0: michael@0: UnicodeString datePattern = fDtpng->getBestPattern(dateSkeleton, status); michael@0: michael@0: concatSingleDate2TimeInterval(dateTimeFormat, dateTimeFormatLength, michael@0: datePattern, UCAL_AM_PM, status); michael@0: concatSingleDate2TimeInterval(dateTimeFormat, dateTimeFormatLength, michael@0: datePattern, UCAL_HOUR, status); michael@0: concatSingleDate2TimeInterval(dateTimeFormat, dateTimeFormatLength, michael@0: datePattern, UCAL_MINUTE, status); michael@0: delete calData; michael@0: } michael@0: } michael@0: michael@0: michael@0: michael@0: void U_EXPORT2 michael@0: DateIntervalFormat::getDateTimeSkeleton(const UnicodeString& skeleton, michael@0: UnicodeString& dateSkeleton, michael@0: UnicodeString& normalizedDateSkeleton, michael@0: UnicodeString& timeSkeleton, michael@0: UnicodeString& normalizedTimeSkeleton) { michael@0: // dateSkeleton follows the sequence of y*M*E*d* michael@0: // timeSkeleton follows the sequence of hm*[v|z]? michael@0: int32_t ECount = 0; michael@0: int32_t dCount = 0; michael@0: int32_t MCount = 0; michael@0: int32_t yCount = 0; michael@0: int32_t hCount = 0; michael@0: int32_t HCount = 0; michael@0: int32_t mCount = 0; michael@0: int32_t vCount = 0; michael@0: int32_t zCount = 0; michael@0: int32_t i; michael@0: michael@0: for (i = 0; i < skeleton.length(); ++i) { michael@0: UChar ch = skeleton[i]; michael@0: switch ( ch ) { michael@0: case CAP_E: michael@0: dateSkeleton.append(ch); michael@0: ++ECount; michael@0: break; michael@0: case LOW_D: michael@0: dateSkeleton.append(ch); michael@0: ++dCount; michael@0: break; michael@0: case CAP_M: michael@0: dateSkeleton.append(ch); michael@0: ++MCount; michael@0: break; michael@0: case LOW_Y: michael@0: dateSkeleton.append(ch); michael@0: ++yCount; michael@0: break; michael@0: case CAP_G: michael@0: case CAP_Y: michael@0: case LOW_U: michael@0: case CAP_Q: michael@0: case LOW_Q: michael@0: case CAP_L: michael@0: case LOW_L: michael@0: case CAP_W: michael@0: case LOW_W: michael@0: case CAP_D: michael@0: case CAP_F: michael@0: case LOW_G: michael@0: case LOW_E: michael@0: case LOW_C: michael@0: normalizedDateSkeleton.append(ch); michael@0: dateSkeleton.append(ch); michael@0: break; michael@0: case LOW_A: michael@0: // 'a' is implicitly handled michael@0: timeSkeleton.append(ch); michael@0: break; michael@0: case LOW_H: michael@0: timeSkeleton.append(ch); michael@0: ++hCount; michael@0: break; michael@0: case CAP_H: michael@0: timeSkeleton.append(ch); michael@0: ++HCount; michael@0: break; michael@0: case LOW_M: michael@0: timeSkeleton.append(ch); michael@0: ++mCount; michael@0: break; michael@0: case LOW_Z: michael@0: ++zCount; michael@0: timeSkeleton.append(ch); michael@0: break; michael@0: case LOW_V: michael@0: ++vCount; michael@0: timeSkeleton.append(ch); michael@0: break; michael@0: case CAP_V: michael@0: case CAP_Z: michael@0: case LOW_K: michael@0: case CAP_K: michael@0: case LOW_J: michael@0: case LOW_S: michael@0: case CAP_S: michael@0: case CAP_A: michael@0: timeSkeleton.append(ch); michael@0: normalizedTimeSkeleton.append(ch); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: /* generate normalized form for date*/ michael@0: if ( yCount != 0 ) { michael@0: for (i = 0; i < yCount; ++i) { michael@0: normalizedDateSkeleton.append(LOW_Y); michael@0: } michael@0: } michael@0: if ( MCount != 0 ) { michael@0: if ( MCount < 3 ) { michael@0: normalizedDateSkeleton.append(CAP_M); michael@0: } else { michael@0: int32_t i; michael@0: for ( i = 0; i < MCount && i < MAX_M_COUNT; ++i ) { michael@0: normalizedDateSkeleton.append(CAP_M); michael@0: } michael@0: } michael@0: } michael@0: if ( ECount != 0 ) { michael@0: if ( ECount <= 3 ) { michael@0: normalizedDateSkeleton.append(CAP_E); michael@0: } else { michael@0: int32_t i; michael@0: for ( i = 0; i < ECount && i < MAX_E_COUNT; ++i ) { michael@0: normalizedDateSkeleton.append(CAP_E); michael@0: } michael@0: } michael@0: } michael@0: if ( dCount != 0 ) { michael@0: normalizedDateSkeleton.append(LOW_D); michael@0: } michael@0: michael@0: /* generate normalized form for time */ michael@0: if ( HCount != 0 ) { michael@0: normalizedTimeSkeleton.append(CAP_H); michael@0: } michael@0: else if ( hCount != 0 ) { michael@0: normalizedTimeSkeleton.append(LOW_H); michael@0: } michael@0: if ( mCount != 0 ) { michael@0: normalizedTimeSkeleton.append(LOW_M); michael@0: } michael@0: if ( zCount != 0 ) { michael@0: normalizedTimeSkeleton.append(LOW_Z); michael@0: } michael@0: if ( vCount != 0 ) { michael@0: normalizedTimeSkeleton.append(LOW_V); michael@0: } michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Generate date or time interval pattern from resource, michael@0: * and set them into the interval pattern locale to this formatter. michael@0: * michael@0: * It needs to handle the following: michael@0: * 1. need to adjust field width. michael@0: * For example, the interval patterns saved in DateIntervalInfo michael@0: * includes "dMMMy", but not "dMMMMy". michael@0: * Need to get interval patterns for dMMMMy from dMMMy. michael@0: * Another example, the interval patterns saved in DateIntervalInfo michael@0: * includes "hmv", but not "hmz". michael@0: * Need to get interval patterns for "hmz' from 'hmv' michael@0: * michael@0: * 2. there might be no pattern for 'y' differ for skeleton "Md", michael@0: * in order to get interval patterns for 'y' differ, michael@0: * need to look for it from skeleton 'yMd' michael@0: * michael@0: * @param dateSkeleton normalized date skeleton michael@0: * @param timeSkeleton normalized time skeleton michael@0: * @return whether the resource is found for the skeleton. michael@0: * TRUE if interval pattern found for the skeleton, michael@0: * FALSE otherwise. michael@0: * @stable ICU 4.0 michael@0: */ michael@0: UBool michael@0: DateIntervalFormat::setSeparateDateTimePtn( michael@0: const UnicodeString& dateSkeleton, michael@0: const UnicodeString& timeSkeleton) { michael@0: const UnicodeString* skeleton; michael@0: // if both date and time skeleton present, michael@0: // the final interval pattern might include time interval patterns michael@0: // ( when, am_pm, hour, minute differ ), michael@0: // but not date interval patterns ( when year, month, day differ ). michael@0: // For year/month/day differ, it falls back to fall-back pattern. michael@0: if ( timeSkeleton.length() != 0 ) { michael@0: skeleton = &timeSkeleton; michael@0: } else { michael@0: skeleton = &dateSkeleton; michael@0: } michael@0: michael@0: /* interval patterns for skeleton "dMMMy" (but not "dMMMMy") michael@0: * are defined in resource, michael@0: * interval patterns for skeleton "dMMMMy" are calculated by michael@0: * 1. get the best match skeleton for "dMMMMy", which is "dMMMy" michael@0: * 2. get the interval patterns for "dMMMy", michael@0: * 3. extend "MMM" to "MMMM" in above interval patterns for "dMMMMy" michael@0: * getBestSkeleton() is step 1. michael@0: */ michael@0: // best skeleton, and the difference information michael@0: int8_t differenceInfo = 0; michael@0: const UnicodeString* bestSkeleton = fInfo->getBestSkeleton(*skeleton, michael@0: differenceInfo); michael@0: /* best skeleton could be NULL. michael@0: For example: in "ca" resource file, michael@0: interval format is defined as following michael@0: intervalFormats{ michael@0: fallback{"{0} - {1}"} michael@0: } michael@0: there is no skeletons/interval patterns defined, michael@0: and the best skeleton match could be NULL michael@0: */ michael@0: if ( bestSkeleton == NULL ) { michael@0: return false; michael@0: } michael@0: michael@0: // difference: michael@0: // 0 means the best matched skeleton is the same as input skeleton michael@0: // 1 means the fields are the same, but field width are different michael@0: // 2 means the only difference between fields are v/z, michael@0: // -1 means there are other fields difference michael@0: if ( differenceInfo == -1 ) { michael@0: // skeleton has different fields, not only v/z difference michael@0: return false; michael@0: } michael@0: michael@0: if ( timeSkeleton.length() == 0 ) { michael@0: UnicodeString extendedSkeleton; michael@0: UnicodeString extendedBestSkeleton; michael@0: // only has date skeleton michael@0: setIntervalPattern(UCAL_DATE, skeleton, bestSkeleton, differenceInfo, michael@0: &extendedSkeleton, &extendedBestSkeleton); michael@0: michael@0: UBool extended = setIntervalPattern(UCAL_MONTH, skeleton, bestSkeleton, michael@0: differenceInfo, michael@0: &extendedSkeleton, &extendedBestSkeleton); michael@0: michael@0: if ( extended ) { michael@0: bestSkeleton = &extendedBestSkeleton; michael@0: skeleton = &extendedSkeleton; michael@0: } michael@0: setIntervalPattern(UCAL_YEAR, skeleton, bestSkeleton, differenceInfo, michael@0: &extendedSkeleton, &extendedBestSkeleton); michael@0: } else { michael@0: setIntervalPattern(UCAL_MINUTE, skeleton, bestSkeleton, differenceInfo); michael@0: setIntervalPattern(UCAL_HOUR, skeleton, bestSkeleton, differenceInfo); michael@0: setIntervalPattern(UCAL_AM_PM, skeleton, bestSkeleton, differenceInfo); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: michael@0: michael@0: void michael@0: DateIntervalFormat::setFallbackPattern(UCalendarDateFields field, michael@0: const UnicodeString& skeleton, michael@0: UErrorCode& status) { michael@0: if ( U_FAILURE(status) ) { michael@0: return; michael@0: } michael@0: UnicodeString pattern = fDtpng->getBestPattern(skeleton, status); michael@0: if ( U_FAILURE(status) ) { michael@0: return; michael@0: } michael@0: setPatternInfo(field, NULL, &pattern, fInfo->getDefaultOrder()); michael@0: } michael@0: michael@0: michael@0: michael@0: michael@0: void michael@0: DateIntervalFormat::setPatternInfo(UCalendarDateFields field, michael@0: const UnicodeString* firstPart, michael@0: const UnicodeString* secondPart, michael@0: UBool laterDateFirst) { michael@0: // for fall back interval patterns, michael@0: // the first part of the pattern is empty, michael@0: // the second part of the pattern is the full-pattern michael@0: // should be used in fall-back. michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: // following should not set any wrong status. michael@0: int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field, michael@0: status); michael@0: if ( U_FAILURE(status) ) { michael@0: return; michael@0: } michael@0: PatternInfo& ptn = fIntervalPatterns[itvPtnIndex]; michael@0: if ( firstPart ) { michael@0: ptn.firstPart = *firstPart; michael@0: } michael@0: if ( secondPart ) { michael@0: ptn.secondPart = *secondPart; michael@0: } michael@0: ptn.laterDateFirst = laterDateFirst; michael@0: } michael@0: michael@0: void michael@0: DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, michael@0: const UnicodeString& intervalPattern) { michael@0: UBool order = fInfo->getDefaultOrder(); michael@0: setIntervalPattern(field, intervalPattern, order); michael@0: } michael@0: michael@0: michael@0: void michael@0: DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, michael@0: const UnicodeString& intervalPattern, michael@0: UBool laterDateFirst) { michael@0: const UnicodeString* pattern = &intervalPattern; michael@0: UBool order = laterDateFirst; michael@0: // check for "latestFirst:" or "earliestFirst:" prefix michael@0: int8_t prefixLength = sizeof(gLaterFirstPrefix)/sizeof(gLaterFirstPrefix[0]); michael@0: int8_t earliestFirstLength = sizeof(gEarlierFirstPrefix)/sizeof(gEarlierFirstPrefix[0]); michael@0: UnicodeString realPattern; michael@0: if ( intervalPattern.startsWith(gLaterFirstPrefix, prefixLength) ) { michael@0: order = true; michael@0: intervalPattern.extract(prefixLength, michael@0: intervalPattern.length() - prefixLength, michael@0: realPattern); michael@0: pattern = &realPattern; michael@0: } else if ( intervalPattern.startsWith(gEarlierFirstPrefix, michael@0: earliestFirstLength) ) { michael@0: order = false; michael@0: intervalPattern.extract(earliestFirstLength, michael@0: intervalPattern.length() - earliestFirstLength, michael@0: realPattern); michael@0: pattern = &realPattern; michael@0: } michael@0: michael@0: int32_t splitPoint = splitPatternInto2Part(*pattern); michael@0: michael@0: UnicodeString firstPart; michael@0: UnicodeString secondPart; michael@0: pattern->extract(0, splitPoint, firstPart); michael@0: if ( splitPoint < pattern->length() ) { michael@0: pattern->extract(splitPoint, pattern->length()-splitPoint, secondPart); michael@0: } michael@0: setPatternInfo(field, &firstPart, &secondPart, order); michael@0: } michael@0: michael@0: michael@0: michael@0: michael@0: /** michael@0: * Generate interval pattern from existing resource michael@0: * michael@0: * It not only save the interval patterns, michael@0: * but also return the extended skeleton and its best match skeleton. michael@0: * michael@0: * @param field largest different calendar field michael@0: * @param skeleton skeleton michael@0: * @param bestSkeleton the best match skeleton which has interval pattern michael@0: * defined in resource michael@0: * @param differenceInfo the difference between skeleton and best skeleton michael@0: * 0 means the best matched skeleton is the same as input skeleton michael@0: * 1 means the fields are the same, but field width are different michael@0: * 2 means the only difference between fields are v/z, michael@0: * -1 means there are other fields difference michael@0: * michael@0: * @param extendedSkeleton extended skeleton michael@0: * @param extendedBestSkeleton extended best match skeleton michael@0: * @return whether the interval pattern is found michael@0: * through extending skeleton or not. michael@0: * TRUE if interval pattern is found by michael@0: * extending skeleton, FALSE otherwise. michael@0: * @stable ICU 4.0 michael@0: */ michael@0: UBool michael@0: DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, michael@0: const UnicodeString* skeleton, michael@0: const UnicodeString* bestSkeleton, michael@0: int8_t differenceInfo, michael@0: UnicodeString* extendedSkeleton, michael@0: UnicodeString* extendedBestSkeleton) { michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: // following getIntervalPattern() should not generate error status michael@0: UnicodeString pattern; michael@0: fInfo->getIntervalPattern(*bestSkeleton, field, pattern, status); michael@0: if ( pattern.isEmpty() ) { michael@0: // single date michael@0: if ( SimpleDateFormat::isFieldUnitIgnored(*bestSkeleton, field) ) { michael@0: // do nothing, format will handle it michael@0: return false; michael@0: } michael@0: michael@0: // for 24 hour system, interval patterns in resource file michael@0: // might not include pattern when am_pm differ, michael@0: // which should be the same as hour differ. michael@0: // add it here for simplicity michael@0: if ( field == UCAL_AM_PM ) { michael@0: fInfo->getIntervalPattern(*bestSkeleton, UCAL_HOUR, pattern,status); michael@0: if ( !pattern.isEmpty() ) { michael@0: setIntervalPattern(field, pattern); michael@0: } michael@0: return false; michael@0: } michael@0: // else, looking for pattern when 'y' differ for 'dMMMM' skeleton, michael@0: // first, get best match pattern "MMMd", michael@0: // since there is no pattern for 'y' differs for skeleton 'MMMd', michael@0: // need to look for it from skeleton 'yMMMd', michael@0: // if found, adjust field width in interval pattern from michael@0: // "MMM" to "MMMM". michael@0: UChar fieldLetter = fgCalendarFieldToPatternLetter[field]; michael@0: if ( extendedSkeleton ) { michael@0: *extendedSkeleton = *skeleton; michael@0: *extendedBestSkeleton = *bestSkeleton; michael@0: extendedSkeleton->insert(0, fieldLetter); michael@0: extendedBestSkeleton->insert(0, fieldLetter); michael@0: // for example, looking for patterns when 'y' differ for michael@0: // skeleton "MMMM". michael@0: fInfo->getIntervalPattern(*extendedBestSkeleton,field,pattern,status); michael@0: if ( pattern.isEmpty() && differenceInfo == 0 ) { michael@0: // if there is no skeleton "yMMMM" defined, michael@0: // look for the best match skeleton, for example: "yMMM" michael@0: const UnicodeString* tmpBest = fInfo->getBestSkeleton( michael@0: *extendedBestSkeleton, differenceInfo); michael@0: if ( tmpBest != 0 && differenceInfo != -1 ) { michael@0: fInfo->getIntervalPattern(*tmpBest, field, pattern, status); michael@0: bestSkeleton = tmpBest; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: if ( !pattern.isEmpty() ) { michael@0: if ( differenceInfo != 0 ) { michael@0: UnicodeString adjustIntervalPattern; michael@0: adjustFieldWidth(*skeleton, *bestSkeleton, pattern, differenceInfo, michael@0: adjustIntervalPattern); michael@0: setIntervalPattern(field, adjustIntervalPattern); michael@0: } else { michael@0: setIntervalPattern(field, pattern); michael@0: } michael@0: if ( extendedSkeleton && !extendedSkeleton->isEmpty() ) { michael@0: return TRUE; michael@0: } michael@0: } michael@0: return FALSE; michael@0: } michael@0: michael@0: michael@0: michael@0: int32_t U_EXPORT2 michael@0: DateIntervalFormat::splitPatternInto2Part(const UnicodeString& intervalPattern) { michael@0: UBool inQuote = false; michael@0: UChar prevCh = 0; michael@0: int32_t count = 0; michael@0: michael@0: /* repeatedPattern used to record whether a pattern has already seen. michael@0: It is a pattern applies to first calendar if it is first time seen, michael@0: otherwise, it is a pattern applies to the second calendar michael@0: */ michael@0: UBool patternRepeated[] = michael@0: { michael@0: // A B C D E F G H I J K L M N O michael@0: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, michael@0: // P Q R S T U V W X Y Z michael@0: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, michael@0: // a b c d e f g h i j k l m n o michael@0: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, michael@0: // p q r s t u v w x y z michael@0: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 michael@0: }; michael@0: michael@0: int8_t PATTERN_CHAR_BASE = 0x41; michael@0: michael@0: /* loop through the pattern string character by character looking for michael@0: * the first repeated pattern letter, which breaks the interval pattern michael@0: * into 2 parts. michael@0: */ michael@0: int32_t i; michael@0: UBool foundRepetition = false; michael@0: for (i = 0; i < intervalPattern.length(); ++i) { michael@0: UChar ch = intervalPattern.charAt(i); michael@0: michael@0: if (ch != prevCh && count > 0) { michael@0: // check the repeativeness of pattern letter michael@0: UBool repeated = patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)]; michael@0: if ( repeated == FALSE ) { michael@0: patternRepeated[prevCh - PATTERN_CHAR_BASE] = TRUE; michael@0: } else { michael@0: foundRepetition = true; michael@0: break; michael@0: } michael@0: count = 0; michael@0: } michael@0: if (ch == '\'') { michael@0: // Consecutive single quotes are a single quote literal, michael@0: // either outside of quotes or between quotes michael@0: if ((i+1) < intervalPattern.length() && michael@0: intervalPattern.charAt(i+1) == '\'') { michael@0: ++i; michael@0: } else { michael@0: inQuote = ! inQuote; michael@0: } michael@0: } michael@0: else if (!inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) michael@0: || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { michael@0: // ch is a date-time pattern character michael@0: prevCh = ch; michael@0: ++count; michael@0: } michael@0: } michael@0: // check last pattern char, distinguish michael@0: // "dd MM" ( no repetition ), michael@0: // "d-d"(last char repeated ), and michael@0: // "d-d MM" ( repetition found ) michael@0: if ( count > 0 && foundRepetition == FALSE ) { michael@0: if ( patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)] == FALSE ) { michael@0: count = 0; michael@0: } michael@0: } michael@0: return (i - count); michael@0: } michael@0: michael@0: michael@0: michael@0: UnicodeString& michael@0: DateIntervalFormat::fallbackFormat(Calendar& fromCalendar, michael@0: Calendar& toCalendar, michael@0: UnicodeString& appendTo, michael@0: FieldPosition& pos, michael@0: UErrorCode& status) const { michael@0: if ( U_FAILURE(status) ) { michael@0: return appendTo; michael@0: } michael@0: // the fall back michael@0: // no need delete earlierDate and laterDate since they are adopted michael@0: UnicodeString* earlierDate = new UnicodeString(); michael@0: *earlierDate = fDateFormat->format(fromCalendar, *earlierDate, pos); michael@0: UnicodeString* laterDate = new UnicodeString(); michael@0: *laterDate = fDateFormat->format(toCalendar, *laterDate, pos); michael@0: UnicodeString fallbackPattern; michael@0: fInfo->getFallbackIntervalPattern(fallbackPattern); michael@0: Formattable fmtArray[2]; michael@0: fmtArray[0].adoptString(earlierDate); michael@0: fmtArray[1].adoptString(laterDate); michael@0: michael@0: UnicodeString fallback; michael@0: MessageFormat::format(fallbackPattern, fmtArray, 2, fallback, status); michael@0: if ( U_SUCCESS(status) ) { michael@0: appendTo.append(fallback); michael@0: } michael@0: return appendTo; michael@0: } michael@0: michael@0: michael@0: michael@0: michael@0: UBool U_EXPORT2 michael@0: DateIntervalFormat::fieldExistsInSkeleton(UCalendarDateFields field, michael@0: const UnicodeString& skeleton) michael@0: { michael@0: const UChar fieldChar = fgCalendarFieldToPatternLetter[field]; michael@0: return ( (skeleton.indexOf(fieldChar) == -1)?FALSE:TRUE ) ; michael@0: } michael@0: michael@0: michael@0: michael@0: void U_EXPORT2 michael@0: DateIntervalFormat::adjustFieldWidth(const UnicodeString& inputSkeleton, michael@0: const UnicodeString& bestMatchSkeleton, michael@0: const UnicodeString& bestIntervalPattern, michael@0: int8_t differenceInfo, michael@0: UnicodeString& adjustedPtn) { michael@0: adjustedPtn = bestIntervalPattern; michael@0: int32_t inputSkeletonFieldWidth[] = michael@0: { michael@0: // A B C D E F G H I J K L M N O michael@0: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, michael@0: // P Q R S T U V W X Y Z michael@0: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, michael@0: // a b c d e f g h i j k l m n o michael@0: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, michael@0: // p q r s t u v w x y z michael@0: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 michael@0: }; michael@0: michael@0: int32_t bestMatchSkeletonFieldWidth[] = michael@0: { michael@0: // A B C D E F G H I J K L M N O michael@0: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, michael@0: // P Q R S T U V W X Y Z michael@0: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, michael@0: // a b c d e f g h i j k l m n o michael@0: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, michael@0: // p q r s t u v w x y z michael@0: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 michael@0: }; michael@0: michael@0: DateIntervalInfo::parseSkeleton(inputSkeleton, inputSkeletonFieldWidth); michael@0: DateIntervalInfo::parseSkeleton(bestMatchSkeleton, bestMatchSkeletonFieldWidth); michael@0: if ( differenceInfo == 2 ) { michael@0: adjustedPtn.findAndReplace(UnicodeString((UChar)0x76 /* v */), michael@0: UnicodeString((UChar)0x7a /* z */)); michael@0: } michael@0: michael@0: UBool inQuote = false; michael@0: UChar prevCh = 0; michael@0: int32_t count = 0; michael@0: michael@0: const int8_t PATTERN_CHAR_BASE = 0x41; michael@0: michael@0: // loop through the pattern string character by character michael@0: int32_t adjustedPtnLength = adjustedPtn.length(); michael@0: int32_t i; michael@0: for (i = 0; i < adjustedPtnLength; ++i) { michael@0: UChar ch = adjustedPtn.charAt(i); michael@0: if (ch != prevCh && count > 0) { michael@0: // check the repeativeness of pattern letter michael@0: UChar skeletonChar = prevCh; michael@0: if ( skeletonChar == CAP_L ) { michael@0: // there is no "L" (always be "M") in skeleton, michael@0: // but there is "L" in pattern. michael@0: // for skeleton "M+", the pattern might be "...L..." michael@0: skeletonChar = CAP_M; michael@0: } michael@0: int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; michael@0: int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; michael@0: if ( fieldCount == count && inputFieldCount > fieldCount ) { michael@0: count = inputFieldCount - fieldCount; michael@0: int32_t j; michael@0: for ( j = 0; j < count; ++j ) { michael@0: adjustedPtn.insert(i, prevCh); michael@0: } michael@0: i += count; michael@0: adjustedPtnLength += count; michael@0: } michael@0: count = 0; michael@0: } michael@0: if (ch == '\'') { michael@0: // Consecutive single quotes are a single quote literal, michael@0: // either outside of quotes or between quotes michael@0: if ((i+1) < adjustedPtn.length() && adjustedPtn.charAt(i+1) == '\'') { michael@0: ++i; michael@0: } else { michael@0: inQuote = ! inQuote; michael@0: } michael@0: } michael@0: else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) michael@0: || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { michael@0: // ch is a date-time pattern character michael@0: prevCh = ch; michael@0: ++count; michael@0: } michael@0: } michael@0: if ( count > 0 ) { michael@0: // last item michael@0: // check the repeativeness of pattern letter michael@0: UChar skeletonChar = prevCh; michael@0: if ( skeletonChar == CAP_L ) { michael@0: // there is no "L" (always be "M") in skeleton, michael@0: // but there is "L" in pattern. michael@0: // for skeleton "M+", the pattern might be "...L..." michael@0: skeletonChar = CAP_M; michael@0: } michael@0: int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; michael@0: int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; michael@0: if ( fieldCount == count && inputFieldCount > fieldCount ) { michael@0: count = inputFieldCount - fieldCount; michael@0: int32_t j; michael@0: for ( j = 0; j < count; ++j ) { michael@0: adjustedPtn.append(prevCh); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: michael@0: void michael@0: DateIntervalFormat::concatSingleDate2TimeInterval(const UChar* format, michael@0: int32_t formatLen, michael@0: const UnicodeString& datePattern, michael@0: UCalendarDateFields field, michael@0: UErrorCode& status) { michael@0: // following should not set wrong status michael@0: int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field, michael@0: status); michael@0: if ( U_FAILURE(status) ) { michael@0: return; michael@0: } michael@0: PatternInfo& timeItvPtnInfo = fIntervalPatterns[itvPtnIndex]; michael@0: if ( !timeItvPtnInfo.firstPart.isEmpty() ) { michael@0: // UnicodeString allocated here is adopted, so no need to delete michael@0: UnicodeString* timeIntervalPattern = new UnicodeString(timeItvPtnInfo.firstPart); michael@0: timeIntervalPattern->append(timeItvPtnInfo.secondPart); michael@0: UnicodeString* dateStr = new UnicodeString(datePattern); michael@0: Formattable fmtArray[2]; michael@0: fmtArray[0].adoptString(timeIntervalPattern); michael@0: fmtArray[1].adoptString(dateStr); michael@0: UnicodeString combinedPattern; michael@0: MessageFormat::format(UnicodeString(TRUE, format, formatLen), michael@0: fmtArray, 2, combinedPattern, status); michael@0: if ( U_FAILURE(status) ) { michael@0: return; michael@0: } michael@0: setIntervalPattern(field, combinedPattern, timeItvPtnInfo.laterDateFirst); michael@0: } michael@0: // else: fall back michael@0: // it should not happen if the interval format defined is valid michael@0: } michael@0: michael@0: michael@0: michael@0: const UChar michael@0: DateIntervalFormat::fgCalendarFieldToPatternLetter[] = michael@0: { michael@0: /*GyM*/ CAP_G, LOW_Y, CAP_M, michael@0: /*wWd*/ LOW_W, CAP_W, LOW_D, michael@0: /*DEF*/ CAP_D, CAP_E, CAP_F, michael@0: /*ahH*/ LOW_A, LOW_H, CAP_H, michael@0: /*m..*/ LOW_M, michael@0: }; michael@0: michael@0: michael@0: U_NAMESPACE_END michael@0: michael@0: #endif