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