Home | History | Annotate | Download | only in i18n
      1 /*******************************************************************************
      2 * Copyright (C) 2008-2010, International Business Machines Corporation and
      3 * others. All Rights Reserved.
      4 *******************************************************************************
      5 *
      6 * File DTITVFMT.CPP
      7 *
      8 *******************************************************************************
      9 */
     10 
     11 #include "unicode/dtitvfmt.h"
     12 
     13 #if !UCONFIG_NO_FORMATTING
     14 
     15 //TODO: put in compilation
     16 //#define DTITVFMT_DEBUG 1
     17 
     18 #include "cstring.h"
     19 #include "unicode/msgfmt.h"
     20 #include "unicode/dtptngen.h"
     21 #include "unicode/dtitvinf.h"
     22 #include "unicode/calendar.h"
     23 #include "dtitv_impl.h"
     24 
     25 #ifdef DTITVFMT_DEBUG
     26 #include <iostream>
     27 #include "cstring.h"
     28 #endif
     29 
     30 #include "gregoimp.h"
     31 
     32 U_NAMESPACE_BEGIN
     33 
     34 
     35 
     36 #ifdef DTITVFMT_DEBUG
     37 #define PRINTMESG(msg) { std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; }
     38 #endif
     39 
     40 
     41 static const UChar gDateFormatSkeleton[][11] = {
     42 //yMMMMEEEEd
     43 {LOW_Y, CAP_M, CAP_M, CAP_M, CAP_M, CAP_E, CAP_E, CAP_E, CAP_E, LOW_D, 0},
     44 //yMMMMd
     45 {LOW_Y, CAP_M, CAP_M, CAP_M, CAP_M, LOW_D, 0},
     46 //yMMMd
     47 {LOW_Y, CAP_M, CAP_M, CAP_M, LOW_D, 0},
     48 //yMd
     49 {LOW_Y, CAP_M, LOW_D, 0} };
     50 
     51 
     52 static const char gDateTimePatternsTag[]="DateTimePatterns";
     53 
     54 
     55 // latestFirst:
     56 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};
     57 
     58 // earliestFirst:
     59 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};
     60 
     61 
     62 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalFormat)
     63 
     64 
     65 
     66 DateIntervalFormat* U_EXPORT2
     67 DateIntervalFormat::createInstance(const UnicodeString& skeleton,
     68                                    UErrorCode& status) {
     69     return createInstance(skeleton, Locale::getDefault(), status);
     70 }
     71 
     72 
     73 DateIntervalFormat* U_EXPORT2
     74 DateIntervalFormat::createInstance(const UnicodeString& skeleton,
     75                                    const Locale& locale,
     76                                    UErrorCode& status) {
     77 #ifdef DTITVFMT_DEBUG
     78     char result[1000];
     79     char result_1[1000];
     80     char mesg[2000];
     81     skeleton.extract(0,  skeleton.length(), result, "UTF-8");
     82     UnicodeString pat;
     83     ((SimpleDateFormat*)dtfmt)->toPattern(pat);
     84     pat.extract(0,  pat.length(), result_1, "UTF-8");
     85     sprintf(mesg, "skeleton: %s; pattern: %s\n", result, result_1);
     86     PRINTMESG(mesg)
     87 #endif
     88 
     89     DateIntervalInfo* dtitvinf = new DateIntervalInfo(locale, status);
     90     return create(locale, dtitvinf, &skeleton, status);
     91 }
     92 
     93 
     94 
     95 DateIntervalFormat* U_EXPORT2
     96 DateIntervalFormat::createInstance(const UnicodeString& skeleton,
     97                                    const DateIntervalInfo& dtitvinf,
     98                                    UErrorCode& status) {
     99     return createInstance(skeleton, Locale::getDefault(), dtitvinf, status);
    100 }
    101 
    102 
    103 DateIntervalFormat* U_EXPORT2
    104 DateIntervalFormat::createInstance(const UnicodeString& skeleton,
    105                                    const Locale& locale,
    106                                    const DateIntervalInfo& dtitvinf,
    107                                    UErrorCode& status) {
    108     DateIntervalInfo* ptn = dtitvinf.clone();
    109     return create(locale, ptn, &skeleton, status);
    110 }
    111 
    112 
    113 DateIntervalFormat::DateIntervalFormat()
    114 :   fInfo(NULL),
    115     fDateFormat(NULL),
    116     fFromCalendar(NULL),
    117     fToCalendar(NULL),
    118     fDtpng(NULL)
    119 {}
    120 
    121 
    122 DateIntervalFormat::DateIntervalFormat(const DateIntervalFormat& itvfmt)
    123 :   Format(itvfmt),
    124     fInfo(NULL),
    125     fDateFormat(NULL),
    126     fFromCalendar(NULL),
    127     fToCalendar(NULL),
    128     fDtpng(NULL) {
    129     *this = itvfmt;
    130 }
    131 
    132 
    133 DateIntervalFormat&
    134 DateIntervalFormat::operator=(const DateIntervalFormat& itvfmt) {
    135     if ( this != &itvfmt ) {
    136         delete fDateFormat;
    137         delete fInfo;
    138         delete fFromCalendar;
    139         delete fToCalendar;
    140         delete fDtpng;
    141         if ( itvfmt.fDateFormat ) {
    142             fDateFormat = (SimpleDateFormat*)itvfmt.fDateFormat->clone();
    143         } else {
    144             fDateFormat = NULL;
    145         }
    146         if ( itvfmt.fInfo ) {
    147             fInfo = itvfmt.fInfo->clone();
    148         } else {
    149             fInfo = NULL;
    150         }
    151         if ( itvfmt.fFromCalendar ) {
    152             fFromCalendar = itvfmt.fFromCalendar->clone();
    153         } else {
    154             fFromCalendar = NULL;
    155         }
    156         if ( itvfmt.fToCalendar ) {
    157             fToCalendar = itvfmt.fToCalendar->clone();
    158         } else {
    159             fToCalendar = NULL;
    160         }
    161         fSkeleton = itvfmt.fSkeleton;
    162         int8_t i;
    163         for ( i = 0; i< DateIntervalInfo::kIPI_MAX_INDEX; ++i ) {
    164             fIntervalPatterns[i] = itvfmt.fIntervalPatterns[i];
    165         }
    166         if (itvfmt.fDtpng) {
    167             fDtpng = itvfmt.fDtpng->clone();
    168         }
    169     }
    170     return *this;
    171 }
    172 
    173 
    174 DateIntervalFormat::~DateIntervalFormat() {
    175     delete fInfo;
    176     delete fDateFormat;
    177     delete fFromCalendar;
    178     delete fToCalendar;
    179     delete fDtpng;
    180 }
    181 
    182 
    183 Format*
    184 DateIntervalFormat::clone(void) const {
    185     return new DateIntervalFormat(*this);
    186 }
    187 
    188 
    189 UBool
    190 DateIntervalFormat::operator==(const Format& other) const {
    191     if ( other.getDynamicClassID() == DateIntervalFormat::getStaticClassID() ) {
    192         DateIntervalFormat* fmt = (DateIntervalFormat*)&other;
    193 #ifdef DTITVFMT_DEBUG
    194     UBool equal;
    195     equal = (this == fmt);
    196 
    197     equal = (*fInfo == *fmt->fInfo);
    198     equal = (*fDateFormat == *fmt->fDateFormat);
    199     equal = fFromCalendar->isEquivalentTo(*fmt->fFromCalendar) ;
    200     equal = fToCalendar->isEquivalentTo(*fmt->fToCalendar) ;
    201     equal = (fSkeleton == fmt->fSkeleton);
    202 #endif
    203         UBool res;
    204         res =  ( this == fmt ) ||
    205                ( Format::operator==(other) &&
    206                  fInfo &&
    207                  ( *fInfo == *fmt->fInfo ) &&
    208                  fDateFormat &&
    209                  ( *fDateFormat == *fmt->fDateFormat ) &&
    210                  fFromCalendar &&
    211                  fFromCalendar->isEquivalentTo(*fmt->fFromCalendar) &&
    212                  fToCalendar &&
    213                  fToCalendar->isEquivalentTo(*fmt->fToCalendar) &&
    214                  fSkeleton == fmt->fSkeleton &&
    215                  fDtpng &&
    216                  (*fDtpng == *fmt->fDtpng) );
    217         int8_t i;
    218         for (i = 0; i< DateIntervalInfo::kIPI_MAX_INDEX && res == TRUE; ++i ) {
    219             res =   ( fIntervalPatterns[i].firstPart ==
    220                       fmt->fIntervalPatterns[i].firstPart) &&
    221                     ( fIntervalPatterns[i].secondPart ==
    222                       fmt->fIntervalPatterns[i].secondPart ) &&
    223                     ( fIntervalPatterns[i].laterDateFirst ==
    224                       fmt->fIntervalPatterns[i].laterDateFirst) ;
    225         }
    226         return res;
    227     }
    228     return FALSE;
    229 }
    230 
    231 
    232 
    233 UnicodeString&
    234 DateIntervalFormat::format(const Formattable& obj,
    235                            UnicodeString& appendTo,
    236                            FieldPosition& fieldPosition,
    237                            UErrorCode& status) const {
    238     if ( U_FAILURE(status) ) {
    239         return appendTo;
    240     }
    241 
    242     if ( obj.getType() == Formattable::kObject ) {
    243         const UObject* formatObj = obj.getObject();
    244         if (formatObj->getDynamicClassID() == DateInterval::getStaticClassID()){
    245             return format((DateInterval*)formatObj, appendTo, fieldPosition, status);
    246         }
    247     }
    248     status = U_ILLEGAL_ARGUMENT_ERROR;
    249     return appendTo;
    250 }
    251 
    252 
    253 UnicodeString&
    254 DateIntervalFormat::format(const DateInterval* dtInterval,
    255                            UnicodeString& appendTo,
    256                            FieldPosition& fieldPosition,
    257                            UErrorCode& status) const {
    258     if ( U_FAILURE(status) ) {
    259         return appendTo;
    260     }
    261 
    262     if ( fFromCalendar != NULL && fToCalendar != NULL &&
    263          fDateFormat != NULL && fInfo != NULL ) {
    264         fFromCalendar->setTime(dtInterval->getFromDate(), status);
    265         fToCalendar->setTime(dtInterval->getToDate(), status);
    266         if ( U_SUCCESS(status) ) {
    267             return format(*fFromCalendar, *fToCalendar, appendTo,fieldPosition, status);
    268         }
    269     }
    270     return appendTo;
    271 }
    272 
    273 
    274 UnicodeString&
    275 DateIntervalFormat::format(Calendar& fromCalendar,
    276                            Calendar& toCalendar,
    277                            UnicodeString& appendTo,
    278                            FieldPosition& pos,
    279                            UErrorCode& status) const {
    280     if ( U_FAILURE(status) ) {
    281         return appendTo;
    282     }
    283 
    284     // not support different calendar types and time zones
    285     //if ( fromCalendar.getType() != toCalendar.getType() ) {
    286     if ( !fromCalendar.isEquivalentTo(toCalendar) ) {
    287         status = U_ILLEGAL_ARGUMENT_ERROR;
    288         return appendTo;
    289     }
    290 
    291     // First, find the largest different calendar field.
    292     UCalendarDateFields field = UCAL_FIELD_COUNT;
    293 
    294     if ( fromCalendar.get(UCAL_ERA,status) != toCalendar.get(UCAL_ERA,status)) {
    295         field = UCAL_ERA;
    296     } else if ( fromCalendar.get(UCAL_YEAR, status) !=
    297                 toCalendar.get(UCAL_YEAR, status) ) {
    298         field = UCAL_YEAR;
    299     } else if ( fromCalendar.get(UCAL_MONTH, status) !=
    300                 toCalendar.get(UCAL_MONTH, status) ) {
    301         field = UCAL_MONTH;
    302     } else if ( fromCalendar.get(UCAL_DATE, status) !=
    303                 toCalendar.get(UCAL_DATE, status) ) {
    304         field = UCAL_DATE;
    305     } else if ( fromCalendar.get(UCAL_AM_PM, status) !=
    306                 toCalendar.get(UCAL_AM_PM, status) ) {
    307         field = UCAL_AM_PM;
    308     } else if ( fromCalendar.get(UCAL_HOUR, status) !=
    309                 toCalendar.get(UCAL_HOUR, status) ) {
    310         field = UCAL_HOUR;
    311     } else if ( fromCalendar.get(UCAL_MINUTE, status) !=
    312                 toCalendar.get(UCAL_MINUTE, status) ) {
    313         field = UCAL_MINUTE;
    314     }
    315 
    316     if ( U_FAILURE(status) ) {
    317         return appendTo;
    318     }
    319     if ( field == UCAL_FIELD_COUNT ) {
    320         /* ignore the second/millisecond etc. small fields' difference.
    321          * use single date when all the above are the same.
    322          */
    323         return fDateFormat->format(fromCalendar, appendTo, pos);
    324     }
    325 
    326     // following call should not set wrong status,
    327     // all the pass-in fields are valid till here
    328     int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field,
    329                                                                         status);
    330     const PatternInfo& intervalPattern = fIntervalPatterns[itvPtnIndex];
    331 
    332     if ( intervalPattern.firstPart.isEmpty() &&
    333          intervalPattern.secondPart.isEmpty() ) {
    334         if ( fDateFormat->isFieldUnitIgnored(field) ) {
    335             /* the largest different calendar field is small than
    336              * the smallest calendar field in pattern,
    337              * return single date format.
    338              */
    339             return fDateFormat->format(fromCalendar, appendTo, pos);
    340         }
    341         return fallbackFormat(fromCalendar, toCalendar, appendTo, pos, status);
    342     }
    343     // If the first part in interval pattern is empty,
    344     // the 2nd part of it saves the full-pattern used in fall-back.
    345     // For a 'real' interval pattern, the first part will never be empty.
    346     if ( intervalPattern.firstPart.isEmpty() ) {
    347         // fall back
    348         UnicodeString originalPattern;
    349         fDateFormat->toPattern(originalPattern);
    350         fDateFormat->applyPattern(intervalPattern.secondPart);
    351         appendTo = fallbackFormat(fromCalendar, toCalendar, appendTo, pos, status);
    352         fDateFormat->applyPattern(originalPattern);
    353         return appendTo;
    354     }
    355     Calendar* firstCal;
    356     Calendar* secondCal;
    357     if ( intervalPattern.laterDateFirst ) {
    358         firstCal = &toCalendar;
    359         secondCal = &fromCalendar;
    360     } else {
    361         firstCal = &fromCalendar;
    362         secondCal = &toCalendar;
    363     }
    364     // break the interval pattern into 2 parts,
    365     // first part should not be empty,
    366     UnicodeString originalPattern;
    367     fDateFormat->toPattern(originalPattern);
    368     fDateFormat->applyPattern(intervalPattern.firstPart);
    369     fDateFormat->format(*firstCal, appendTo, pos);
    370     if ( !intervalPattern.secondPart.isEmpty() ) {
    371         fDateFormat->applyPattern(intervalPattern.secondPart);
    372         fDateFormat->format(*secondCal, appendTo, pos);
    373     }
    374     fDateFormat->applyPattern(originalPattern);
    375     return appendTo;
    376 }
    377 
    378 
    379 
    380 void
    381 DateIntervalFormat::parseObject(const UnicodeString& /* source */,
    382                                 Formattable& /* result */,
    383                                 ParsePosition& /* parse_pos */) const {
    384     // parseObject(const UnicodeString&, Formattable&, UErrorCode&) const
    385     // will set status as U_INVALID_FORMAT_ERROR if
    386     // parse_pos is still 0
    387 }
    388 
    389 
    390 
    391 
    392 const DateIntervalInfo*
    393 DateIntervalFormat::getDateIntervalInfo() const {
    394     return fInfo;
    395 }
    396 
    397 
    398 void
    399 DateIntervalFormat::setDateIntervalInfo(const DateIntervalInfo& newItvPattern,
    400                                         UErrorCode& status) {
    401     delete fInfo;
    402     fInfo = new DateIntervalInfo(newItvPattern);
    403     if ( fDateFormat ) {
    404         initializePattern(status);
    405     }
    406 }
    407 
    408 
    409 
    410 const DateFormat*
    411 DateIntervalFormat::getDateFormat() const {
    412     return fDateFormat;
    413 }
    414 
    415 
    416 DateIntervalFormat::DateIntervalFormat(const Locale& locale,
    417                                        DateIntervalInfo* dtItvInfo,
    418                                        const UnicodeString* skeleton,
    419                                        UErrorCode& status)
    420 :   fInfo(NULL),
    421     fDateFormat(NULL),
    422     fFromCalendar(NULL),
    423     fToCalendar(NULL),
    424     fDtpng(NULL)
    425 {
    426     if ( U_FAILURE(status) ) {
    427         delete dtItvInfo;
    428         return;
    429     }
    430     fDtpng = DateTimePatternGenerator::createInstance(locale, status);
    431     SimpleDateFormat* dtfmt = createSDFPatternInstance(*skeleton, locale,
    432                                                     fDtpng, status);
    433     if ( U_FAILURE(status) ) {
    434         delete dtItvInfo;
    435         delete fDtpng;
    436         delete dtfmt;
    437         return;
    438     }
    439     if ( dtfmt == NULL || dtItvInfo == NULL || fDtpng == NULL ) {
    440         status = U_MEMORY_ALLOCATION_ERROR;
    441         // safe to delete NULL
    442         delete dtfmt;
    443         delete dtItvInfo;
    444         delete fDtpng;
    445         return;
    446     }
    447     if ( skeleton ) {
    448         fSkeleton = *skeleton;
    449     }
    450     fInfo = dtItvInfo;
    451     fDateFormat = dtfmt;
    452     if ( dtfmt->getCalendar() ) {
    453         fFromCalendar = dtfmt->getCalendar()->clone();
    454         fToCalendar = dtfmt->getCalendar()->clone();
    455     } else {
    456         fFromCalendar = NULL;
    457         fToCalendar = NULL;
    458     }
    459     initializePattern(status);
    460 }
    461 
    462 
    463 SimpleDateFormat* U_EXPORT2
    464 DateIntervalFormat::createSDFPatternInstance(const UnicodeString& skeleton,
    465                                              const Locale& locale,
    466                                              DateTimePatternGenerator* dtpng,
    467                                              UErrorCode& status)
    468 {
    469     if ( U_FAILURE(status) ) {
    470         return NULL;
    471     }
    472 
    473     const UnicodeString pattern = dtpng->getBestPattern(skeleton, status);
    474     if ( U_FAILURE(status) ) {
    475         return NULL;
    476     }
    477     SimpleDateFormat* dtfmt = new SimpleDateFormat(pattern, locale, status);
    478     if ( U_FAILURE(status) ) {
    479         delete dtfmt;
    480         return NULL;
    481     }
    482     return dtfmt;
    483 }
    484 
    485 
    486 DateIntervalFormat* U_EXPORT2
    487 DateIntervalFormat::create(const Locale& locale,
    488                            DateIntervalInfo* dtitvinf,
    489                            const UnicodeString* skeleton,
    490                            UErrorCode& status) {
    491     DateIntervalFormat* f = new DateIntervalFormat(locale, dtitvinf,
    492                                                    skeleton, status);
    493     if ( f == NULL ) {
    494         status = U_MEMORY_ALLOCATION_ERROR;
    495         delete dtitvinf;
    496     } else if ( U_FAILURE(status) ) {
    497         // safe to delete f, although nothing acutally is saved
    498         delete f;
    499         f = 0;
    500     }
    501     return f;
    502 }
    503 
    504 
    505 
    506 /**
    507  * Initialize interval patterns locale to this formatter
    508  *
    509  * This code is a bit complicated since
    510  * 1. the interval patterns saved in resource bundle files are interval
    511  *    patterns based on date or time only.
    512  *    It does not have interval patterns based on both date and time.
    513  *    Interval patterns on both date and time are algorithm generated.
    514  *
    515  *    For example, it has interval patterns on skeleton "dMy" and "hm",
    516  *    but it does not have interval patterns on skeleton "dMyhm".
    517  *
    518  *    The rule to genearte interval patterns for both date and time skeleton are
    519  *    1) when the year, month, or day differs, concatenate the two original
    520  *    expressions with a separator between,
    521  *    For example, interval pattern from "Jan 10, 2007 10:10 am"
    522  *    to "Jan 11, 2007 10:10am" is
    523  *    "Jan 10, 2007 10:10 am - Jan 11, 2007 10:10am"
    524  *
    525  *    2) otherwise, present the date followed by the range expression
    526  *    for the time.
    527  *    For example, interval pattern from "Jan 10, 2007 10:10 am"
    528  *    to "Jan 10, 2007 11:10am" is
    529  *    "Jan 10, 2007 10:10 am - 11:10am"
    530  *
    531  * 2. even a pattern does not request a certion calendar field,
    532  *    the interval pattern needs to include such field if such fields are
    533  *    different between 2 dates.
    534  *    For example, a pattern/skeleton is "hm", but the interval pattern
    535  *    includes year, month, and date when year, month, and date differs.
    536  *
    537  * @param status          output param set to success/failure code on exit
    538  * @stable ICU 4.0
    539  */
    540 void
    541 DateIntervalFormat::initializePattern(UErrorCode& status) {
    542     if ( U_FAILURE(status) ) {
    543         return;
    544     }
    545     const Locale& locale = fDateFormat->getSmpFmtLocale();
    546     if ( fSkeleton.isEmpty() ) {
    547         UnicodeString fullPattern;
    548         fDateFormat->toPattern(fullPattern);
    549 #ifdef DTITVFMT_DEBUG
    550     char result[1000];
    551     char result_1[1000];
    552     char mesg[2000];
    553     fSkeleton.extract(0,  fSkeleton.length(), result, "UTF-8");
    554     sprintf(mesg, "in getBestSkeleton: fSkeleton: %s; \n", result);
    555     PRINTMESG(mesg)
    556 #endif
    557         // fSkeleton is already set by createDateIntervalInstance()
    558         // or by createInstance(UnicodeString skeleton, .... )
    559         fSkeleton = fDtpng->getSkeleton(fullPattern, status);
    560         if ( U_FAILURE(status) ) {
    561             return;
    562         }
    563     }
    564 
    565     // initialize the fIntervalPattern ordering
    566     int8_t i;
    567     for ( i = 0; i < DateIntervalInfo::kIPI_MAX_INDEX; ++i ) {
    568         fIntervalPatterns[i].laterDateFirst = fInfo->getDefaultOrder();
    569     }
    570 
    571     /* Check whether the skeleton is a combination of date and time.
    572      * For the complication reason 1 explained above.
    573      */
    574     UnicodeString dateSkeleton;
    575     UnicodeString timeSkeleton;
    576     UnicodeString normalizedTimeSkeleton;
    577     UnicodeString normalizedDateSkeleton;
    578 
    579 
    580     /* the difference between time skeleton and normalizedTimeSkeleton are:
    581      * 1. (Formerly, normalized time skeleton folded 'H' to 'h'; no longer true)
    582      * 2. 'a' is omitted in normalized time skeleton.
    583      * 3. there is only one appearance for 'h' or 'H', 'm','v', 'z' in normalized
    584      *    time skeleton
    585      *
    586      * The difference between date skeleton and normalizedDateSkeleton are:
    587      * 1. both 'y' and 'd' appear only once in normalizeDateSkeleton
    588      * 2. 'E' and 'EE' are normalized into 'EEE'
    589      * 3. 'MM' is normalized into 'M'
    590      */
    591     getDateTimeSkeleton(fSkeleton, dateSkeleton, normalizedDateSkeleton,
    592                         timeSkeleton, normalizedTimeSkeleton);
    593 
    594 #ifdef DTITVFMT_DEBUG
    595     char result[1000];
    596     char result_1[1000];
    597     char mesg[2000];
    598     fSkeleton.extract(0,  fSkeleton.length(), result, "UTF-8");
    599     sprintf(mesg, "in getBestSkeleton: fSkeleton: %s; \n", result);
    600     PRINTMESG(mesg)
    601 #endif
    602 
    603 
    604     UBool found = setSeparateDateTimePtn(normalizedDateSkeleton,
    605                                          normalizedTimeSkeleton);
    606 
    607     if ( found == false ) {
    608         // use fallback
    609         // TODO: if user asks "m"(minute), but "d"(day) differ
    610         if ( timeSkeleton.length() != 0 ) {
    611             if ( dateSkeleton.length() == 0 ) {
    612                 // prefix with yMd
    613                 timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort]);
    614                 UnicodeString pattern = fDtpng->getBestPattern(timeSkeleton, status);
    615                 if ( U_FAILURE(status) ) {
    616                     return;
    617                 }
    618                 // for fall back interval patterns,
    619                 // the first part of the pattern is empty,
    620                 // the second part of the pattern is the full-pattern
    621                 // should be used in fall-back.
    622                 setPatternInfo(UCAL_DATE, NULL, &pattern, fInfo->getDefaultOrder());
    623                 setPatternInfo(UCAL_MONTH, NULL, &pattern, fInfo->getDefaultOrder());
    624                 setPatternInfo(UCAL_YEAR, NULL, &pattern, fInfo->getDefaultOrder());
    625             } else {
    626                 // TODO: fall back
    627             }
    628         } else {
    629             // TODO: fall back
    630         }
    631         return;
    632     } // end of skeleton not found
    633     // interval patterns for skeleton are found in resource
    634     if ( timeSkeleton.length() == 0 ) {
    635         // done
    636     } else if ( dateSkeleton.length() == 0 ) {
    637         // prefix with yMd
    638         timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort]);
    639         UnicodeString pattern = fDtpng->getBestPattern(timeSkeleton, status);
    640         if ( U_FAILURE(status) ) {
    641             return;
    642         }
    643         // for fall back interval patterns,
    644         // the first part of the pattern is empty,
    645         // the second part of the pattern is the full-pattern
    646         // should be used in fall-back.
    647         setPatternInfo(UCAL_DATE, NULL, &pattern, fInfo->getDefaultOrder());
    648         setPatternInfo(UCAL_MONTH, NULL, &pattern, fInfo->getDefaultOrder());
    649         setPatternInfo(UCAL_YEAR, NULL, &pattern, fInfo->getDefaultOrder());
    650     } else {
    651         /* if both present,
    652          * 1) when the year, month, or day differs,
    653          * concatenate the two original expressions with a separator between,
    654          * 2) otherwise, present the date followed by the
    655          * range expression for the time.
    656          */
    657         /*
    658          * 1) when the year, month, or day differs,
    659          * concatenate the two original expressions with a separator between,
    660          */
    661         // if field exists, use fall back
    662         UnicodeString skeleton = fSkeleton;
    663         if ( !fieldExistsInSkeleton(UCAL_DATE, dateSkeleton) ) {
    664             // prefix skeleton with 'd'
    665             skeleton.insert(0, LOW_D);
    666             setFallbackPattern(UCAL_DATE, skeleton, status);
    667         }
    668         if ( !fieldExistsInSkeleton(UCAL_MONTH, dateSkeleton) ) {
    669             // then prefix skeleton with 'M'
    670             skeleton.insert(0, CAP_M);
    671             setFallbackPattern(UCAL_MONTH, skeleton, status);
    672         }
    673         if ( !fieldExistsInSkeleton(UCAL_YEAR, dateSkeleton) ) {
    674             // then prefix skeleton with 'y'
    675             skeleton.insert(0, LOW_Y);
    676             setFallbackPattern(UCAL_YEAR, skeleton, status);
    677         }
    678 
    679         /*
    680          * 2) otherwise, present the date followed by the
    681          * range expression for the time.
    682          */
    683         // Need the Date/Time pattern for concatnation the date with
    684         // the time interval.
    685         // The date/time pattern ( such as {0} {1} ) is saved in
    686         // calendar, that is why need to get the CalendarData here.
    687         CalendarData* calData = new CalendarData(locale, NULL, status);
    688 
    689         if ( U_FAILURE(status) ) {
    690             delete calData;
    691             return;
    692         }
    693 
    694         if ( calData == NULL ) {
    695             status = U_MEMORY_ALLOCATION_ERROR;
    696             return;
    697         }
    698 
    699         const UResourceBundle* dateTimePatternsRes = calData->getByKey(
    700                                            gDateTimePatternsTag, status);
    701         int32_t dateTimeFormatLength;
    702         const UChar* dateTimeFormat = ures_getStringByIndex(
    703                                             dateTimePatternsRes,
    704                                             (int32_t)DateFormat::kDateTime,
    705                                             &dateTimeFormatLength, &status);
    706         if ( U_FAILURE(status) ) {
    707             return;
    708         }
    709 
    710         UnicodeString datePattern = fDtpng->getBestPattern(dateSkeleton, status);
    711 
    712         concatSingleDate2TimeInterval(dateTimeFormat, dateTimeFormatLength,
    713                                       datePattern, UCAL_AM_PM, status);
    714         concatSingleDate2TimeInterval(dateTimeFormat, dateTimeFormatLength,
    715                                       datePattern, UCAL_HOUR, status);
    716         concatSingleDate2TimeInterval(dateTimeFormat, dateTimeFormatLength,
    717                                       datePattern, UCAL_MINUTE, status);
    718         delete calData;
    719     }
    720 }
    721 
    722 
    723 
    724 void  U_EXPORT2
    725 DateIntervalFormat::getDateTimeSkeleton(const UnicodeString& skeleton,
    726                                         UnicodeString& dateSkeleton,
    727                                         UnicodeString& normalizedDateSkeleton,
    728                                         UnicodeString& timeSkeleton,
    729                                         UnicodeString& normalizedTimeSkeleton) {
    730     // dateSkeleton follows the sequence of y*M*E*d*
    731     // timeSkeleton follows the sequence of hm*[v|z]?
    732     int32_t ECount = 0;
    733     int32_t dCount = 0;
    734     int32_t MCount = 0;
    735     int32_t yCount = 0;
    736     int32_t hCount = 0;
    737     int32_t HCount = 0;
    738     int32_t mCount = 0;
    739     int32_t vCount = 0;
    740     int32_t zCount = 0;
    741     int32_t i;
    742 
    743     for (i = 0; i < skeleton.length(); ++i) {
    744         UChar ch = skeleton[i];
    745         switch ( ch ) {
    746           case CAP_E:
    747             dateSkeleton.append(ch);
    748             ++ECount;
    749             break;
    750           case LOW_D:
    751             dateSkeleton.append(ch);
    752             ++dCount;
    753             break;
    754           case CAP_M:
    755             dateSkeleton.append(ch);
    756             ++MCount;
    757             break;
    758           case LOW_Y:
    759             dateSkeleton.append(ch);
    760             ++yCount;
    761             break;
    762           case CAP_G:
    763           case CAP_Y:
    764           case LOW_U:
    765           case CAP_Q:
    766           case LOW_Q:
    767           case CAP_L:
    768           case LOW_L:
    769           case CAP_W:
    770           case LOW_W:
    771           case CAP_D:
    772           case CAP_F:
    773           case LOW_G:
    774           case LOW_E:
    775           case LOW_C:
    776             normalizedDateSkeleton.append(ch);
    777             dateSkeleton.append(ch);
    778             break;
    779           case LOW_A:
    780             // 'a' is implicitly handled
    781             timeSkeleton.append(ch);
    782             break;
    783           case LOW_H:
    784             timeSkeleton.append(ch);
    785             ++hCount;
    786             break;
    787           case CAP_H:
    788             timeSkeleton.append(ch);
    789             ++HCount;
    790             break;
    791           case LOW_M:
    792             timeSkeleton.append(ch);
    793             ++mCount;
    794             break;
    795           case LOW_Z:
    796             ++zCount;
    797             timeSkeleton.append(ch);
    798             break;
    799           case LOW_V:
    800             ++vCount;
    801             timeSkeleton.append(ch);
    802             break;
    803           case CAP_V:
    804           case CAP_Z:
    805           case LOW_K:
    806           case CAP_K:
    807           case LOW_J:
    808           case LOW_S:
    809           case CAP_S:
    810           case CAP_A:
    811             timeSkeleton.append(ch);
    812             normalizedTimeSkeleton.append(ch);
    813             break;
    814         }
    815     }
    816 
    817     /* generate normalized form for date*/
    818     if ( yCount != 0 ) {
    819         normalizedDateSkeleton.append(LOW_Y);
    820     }
    821     if ( MCount != 0 ) {
    822         if ( MCount < 3 ) {
    823             normalizedDateSkeleton.append(CAP_M);
    824         } else {
    825             int32_t i;
    826             for ( i = 0; i < MCount && i < MAX_M_COUNT; ++i ) {
    827                  normalizedDateSkeleton.append(CAP_M);
    828             }
    829         }
    830     }
    831     if ( ECount != 0 ) {
    832         if ( ECount <= 3 ) {
    833             normalizedDateSkeleton.append(CAP_E);
    834         } else {
    835             int32_t i;
    836             for ( i = 0; i < ECount && i < MAX_E_COUNT; ++i ) {
    837                  normalizedDateSkeleton.append(CAP_E);
    838             }
    839         }
    840     }
    841     if ( dCount != 0 ) {
    842         normalizedDateSkeleton.append(LOW_D);
    843     }
    844 
    845     /* generate normalized form for time */
    846     if ( HCount != 0 ) {
    847         normalizedTimeSkeleton.append(CAP_H);
    848     }
    849     else if ( hCount != 0 ) {
    850         normalizedTimeSkeleton.append(LOW_H);
    851     }
    852     if ( mCount != 0 ) {
    853         normalizedTimeSkeleton.append(LOW_M);
    854     }
    855     if ( zCount != 0 ) {
    856         normalizedTimeSkeleton.append(LOW_Z);
    857     }
    858     if ( vCount != 0 ) {
    859         normalizedTimeSkeleton.append(LOW_V);
    860     }
    861 }
    862 
    863 
    864 /**
    865  * Generate date or time interval pattern from resource,
    866  * and set them into the interval pattern locale to this formatter.
    867  *
    868  * It needs to handle the following:
    869  * 1. need to adjust field width.
    870  *    For example, the interval patterns saved in DateIntervalInfo
    871  *    includes "dMMMy", but not "dMMMMy".
    872  *    Need to get interval patterns for dMMMMy from dMMMy.
    873  *    Another example, the interval patterns saved in DateIntervalInfo
    874  *    includes "hmv", but not "hmz".
    875  *    Need to get interval patterns for "hmz' from 'hmv'
    876  *
    877  * 2. there might be no pattern for 'y' differ for skeleton "Md",
    878  *    in order to get interval patterns for 'y' differ,
    879  *    need to look for it from skeleton 'yMd'
    880  *
    881  * @param dateSkeleton   normalized date skeleton
    882  * @param timeSkeleton   normalized time skeleton
    883  * @return               whether the resource is found for the skeleton.
    884  *                       TRUE if interval pattern found for the skeleton,
    885  *                       FALSE otherwise.
    886  * @stable ICU 4.0
    887  */
    888 UBool
    889 DateIntervalFormat::setSeparateDateTimePtn(
    890                                  const UnicodeString& dateSkeleton,
    891                                  const UnicodeString& timeSkeleton) {
    892     const UnicodeString* skeleton;
    893     // if both date and time skeleton present,
    894     // the final interval pattern might include time interval patterns
    895     // ( when, am_pm, hour, minute differ ),
    896     // but not date interval patterns ( when year, month, day differ ).
    897     // For year/month/day differ, it falls back to fall-back pattern.
    898     if ( timeSkeleton.length() != 0  ) {
    899         skeleton = &timeSkeleton;
    900     } else {
    901         skeleton = &dateSkeleton;
    902     }
    903 
    904     /* interval patterns for skeleton "dMMMy" (but not "dMMMMy")
    905      * are defined in resource,
    906      * interval patterns for skeleton "dMMMMy" are calculated by
    907      * 1. get the best match skeleton for "dMMMMy", which is "dMMMy"
    908      * 2. get the interval patterns for "dMMMy",
    909      * 3. extend "MMM" to "MMMM" in above interval patterns for "dMMMMy"
    910      * getBestSkeleton() is step 1.
    911      */
    912     // best skeleton, and the difference information
    913     int8_t differenceInfo = 0;
    914     const UnicodeString* bestSkeleton = fInfo->getBestSkeleton(*skeleton,
    915                                                                differenceInfo);
    916     /* best skeleton could be NULL.
    917        For example: in "ca" resource file,
    918        interval format is defined as following
    919            intervalFormats{
    920                 fallback{"{0} - {1}"}
    921             }
    922        there is no skeletons/interval patterns defined,
    923        and the best skeleton match could be NULL
    924      */
    925     if ( bestSkeleton == NULL ) {
    926         return false;
    927     }
    928 
    929     // difference:
    930     // 0 means the best matched skeleton is the same as input skeleton
    931     // 1 means the fields are the same, but field width are different
    932     // 2 means the only difference between fields are v/z,
    933     // -1 means there are other fields difference
    934     if ( differenceInfo == -1 ) {
    935         // skeleton has different fields, not only  v/z difference
    936         return false;
    937     }
    938 
    939     if ( timeSkeleton.length() == 0 ) {
    940         UnicodeString extendedSkeleton;
    941         UnicodeString extendedBestSkeleton;
    942         // only has date skeleton
    943         setIntervalPattern(UCAL_DATE, skeleton, bestSkeleton, differenceInfo,
    944                            &extendedSkeleton, &extendedBestSkeleton);
    945 
    946         UBool extended = setIntervalPattern(UCAL_MONTH, skeleton, bestSkeleton,
    947                                      differenceInfo,
    948                                      &extendedSkeleton, &extendedBestSkeleton);
    949 
    950         if ( extended ) {
    951             bestSkeleton = &extendedBestSkeleton;
    952             skeleton = &extendedSkeleton;
    953         }
    954         setIntervalPattern(UCAL_YEAR, skeleton, bestSkeleton, differenceInfo,
    955                            &extendedSkeleton, &extendedBestSkeleton);
    956     } else {
    957         setIntervalPattern(UCAL_MINUTE, skeleton, bestSkeleton, differenceInfo);
    958         setIntervalPattern(UCAL_HOUR, skeleton, bestSkeleton, differenceInfo);
    959         setIntervalPattern(UCAL_AM_PM, skeleton, bestSkeleton, differenceInfo);
    960     }
    961     return true;
    962 }
    963 
    964 
    965 
    966 void
    967 DateIntervalFormat::setFallbackPattern(UCalendarDateFields field,
    968                                        const UnicodeString& skeleton,
    969                                        UErrorCode& status) {
    970     if ( U_FAILURE(status) ) {
    971         return;
    972     }
    973     UnicodeString pattern = fDtpng->getBestPattern(skeleton, status);
    974     if ( U_FAILURE(status) ) {
    975         return;
    976     }
    977     setPatternInfo(field, NULL, &pattern, fInfo->getDefaultOrder());
    978 }
    979 
    980 
    981 
    982 
    983 void
    984 DateIntervalFormat::setPatternInfo(UCalendarDateFields field,
    985                                    const UnicodeString* firstPart,
    986                                    const UnicodeString* secondPart,
    987                                    UBool laterDateFirst) {
    988     // for fall back interval patterns,
    989     // the first part of the pattern is empty,
    990     // the second part of the pattern is the full-pattern
    991     // should be used in fall-back.
    992     UErrorCode status = U_ZERO_ERROR;
    993     // following should not set any wrong status.
    994     int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field,
    995                                                                         status);
    996     if ( U_FAILURE(status) ) {
    997         return;
    998     }
    999     PatternInfo& ptn = fIntervalPatterns[itvPtnIndex];
   1000     if ( firstPart ) {
   1001         ptn.firstPart = *firstPart;
   1002     }
   1003     if ( secondPart ) {
   1004         ptn.secondPart = *secondPart;
   1005     }
   1006     ptn.laterDateFirst = laterDateFirst;
   1007 }
   1008 
   1009 void
   1010 DateIntervalFormat::setIntervalPattern(UCalendarDateFields field,
   1011                                        const UnicodeString& intervalPattern) {
   1012     UBool order = fInfo->getDefaultOrder();
   1013     setIntervalPattern(field, intervalPattern, order);
   1014 }
   1015 
   1016 
   1017 void
   1018 DateIntervalFormat::setIntervalPattern(UCalendarDateFields field,
   1019                                        const UnicodeString& intervalPattern,
   1020                                        UBool laterDateFirst) {
   1021     const UnicodeString* pattern = &intervalPattern;
   1022     UBool order = laterDateFirst;
   1023     // check for "latestFirst:" or "earliestFirst:" prefix
   1024     int8_t prefixLength = sizeof(gLaterFirstPrefix)/sizeof(gLaterFirstPrefix[0]);
   1025     int8_t earliestFirstLength = sizeof(gEarlierFirstPrefix)/sizeof(gEarlierFirstPrefix[0]);
   1026     UnicodeString realPattern;
   1027     if ( intervalPattern.startsWith(gLaterFirstPrefix, prefixLength) ) {
   1028         order = true;
   1029         intervalPattern.extract(prefixLength,
   1030                                 intervalPattern.length() - prefixLength,
   1031                                 realPattern);
   1032         pattern = &realPattern;
   1033     } else if ( intervalPattern.startsWith(gEarlierFirstPrefix,
   1034                                            earliestFirstLength) ) {
   1035         order = false;
   1036         intervalPattern.extract(earliestFirstLength,
   1037                                 intervalPattern.length() - earliestFirstLength,
   1038                                 realPattern);
   1039         pattern = &realPattern;
   1040     }
   1041 
   1042     int32_t splitPoint = splitPatternInto2Part(*pattern);
   1043 
   1044     UnicodeString firstPart;
   1045     UnicodeString secondPart;
   1046     pattern->extract(0, splitPoint, firstPart);
   1047     if ( splitPoint < pattern->length() ) {
   1048         pattern->extract(splitPoint, pattern->length()-splitPoint, secondPart);
   1049     }
   1050     setPatternInfo(field, &firstPart, &secondPart, order);
   1051 }
   1052 
   1053 
   1054 
   1055 
   1056 /**
   1057  * Generate interval pattern from existing resource
   1058  *
   1059  * It not only save the interval patterns,
   1060  * but also return the extended skeleton and its best match skeleton.
   1061  *
   1062  * @param field           largest different calendar field
   1063  * @param skeleton        skeleton
   1064  * @param bestSkeleton    the best match skeleton which has interval pattern
   1065  *                        defined in resource
   1066  * @param differenceInfo  the difference between skeleton and best skeleton
   1067  *         0 means the best matched skeleton is the same as input skeleton
   1068  *         1 means the fields are the same, but field width are different
   1069  *         2 means the only difference between fields are v/z,
   1070  *        -1 means there are other fields difference
   1071  *
   1072  * @param extendedSkeleton      extended skeleton
   1073  * @param extendedBestSkeleton  extended best match skeleton
   1074  * @return                      whether the interval pattern is found
   1075  *                              through extending skeleton or not.
   1076  *                              TRUE if interval pattern is found by
   1077  *                              extending skeleton, FALSE otherwise.
   1078  * @stable ICU 4.0
   1079  */
   1080 UBool
   1081 DateIntervalFormat::setIntervalPattern(UCalendarDateFields field,
   1082                                        const UnicodeString* skeleton,
   1083                                        const UnicodeString* bestSkeleton,
   1084                                        int8_t differenceInfo,
   1085                                        UnicodeString* extendedSkeleton,
   1086                                        UnicodeString* extendedBestSkeleton) {
   1087     UErrorCode status = U_ZERO_ERROR;
   1088     // following getIntervalPattern() should not generate error status
   1089     UnicodeString pattern;
   1090     fInfo->getIntervalPattern(*bestSkeleton, field, pattern, status);
   1091     if ( pattern.isEmpty() ) {
   1092         // single date
   1093         if ( SimpleDateFormat::isFieldUnitIgnored(*bestSkeleton, field) ) {
   1094             // do nothing, format will handle it
   1095             return false;
   1096         }
   1097 
   1098         // for 24 hour system, interval patterns in resource file
   1099         // might not include pattern when am_pm differ,
   1100         // which should be the same as hour differ.
   1101         // add it here for simplicity
   1102         if ( field == UCAL_AM_PM ) {
   1103             fInfo->getIntervalPattern(*bestSkeleton, UCAL_HOUR, pattern,status);
   1104             if ( !pattern.isEmpty() ) {
   1105                 setIntervalPattern(field, pattern);
   1106             }
   1107             return false;
   1108         }
   1109         // else, looking for pattern when 'y' differ for 'dMMMM' skeleton,
   1110         // first, get best match pattern "MMMd",
   1111         // since there is no pattern for 'y' differs for skeleton 'MMMd',
   1112         // need to look for it from skeleton 'yMMMd',
   1113         // if found, adjust field width in interval pattern from
   1114         // "MMM" to "MMMM".
   1115         UChar fieldLetter = fgCalendarFieldToPatternLetter[field];
   1116         if ( extendedSkeleton ) {
   1117             *extendedSkeleton = *skeleton;
   1118             *extendedBestSkeleton = *bestSkeleton;
   1119             extendedSkeleton->insert(0, fieldLetter);
   1120             extendedBestSkeleton->insert(0, fieldLetter);
   1121             // for example, looking for patterns when 'y' differ for
   1122             // skeleton "MMMM".
   1123             fInfo->getIntervalPattern(*extendedBestSkeleton,field,pattern,status);
   1124             if ( pattern.isEmpty() && differenceInfo == 0 ) {
   1125                 // if there is no skeleton "yMMMM" defined,
   1126                 // look for the best match skeleton, for example: "yMMM"
   1127                 const UnicodeString* tmpBest = fInfo->getBestSkeleton(
   1128                                         *extendedBestSkeleton, differenceInfo);
   1129                 if ( tmpBest != 0 && differenceInfo != -1 ) {
   1130                     fInfo->getIntervalPattern(*tmpBest, field, pattern, status);
   1131                     bestSkeleton = tmpBest;
   1132                 }
   1133             }
   1134         }
   1135     }
   1136     if ( !pattern.isEmpty() ) {
   1137         if ( differenceInfo != 0 ) {
   1138             UnicodeString adjustIntervalPattern;
   1139             adjustFieldWidth(*skeleton, *bestSkeleton, pattern, differenceInfo,
   1140                               adjustIntervalPattern);
   1141             setIntervalPattern(field, adjustIntervalPattern);
   1142         } else {
   1143             setIntervalPattern(field, pattern);
   1144         }
   1145         if ( extendedSkeleton && !extendedSkeleton->isEmpty() ) {
   1146             return TRUE;
   1147         }
   1148     }
   1149     return FALSE;
   1150 }
   1151 
   1152 
   1153 
   1154 int32_t  U_EXPORT2
   1155 DateIntervalFormat::splitPatternInto2Part(const UnicodeString& intervalPattern) {
   1156     UBool inQuote = false;
   1157     UChar prevCh = 0;
   1158     int32_t count = 0;
   1159 
   1160     /* repeatedPattern used to record whether a pattern has already seen.
   1161        It is a pattern applies to first calendar if it is first time seen,
   1162        otherwise, it is a pattern applies to the second calendar
   1163      */
   1164     UBool patternRepeated[] =
   1165     {
   1166     //       A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
   1167              0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
   1168     //   P   Q   R   S   T   U   V   W   X   Y   Z
   1169          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 0, 0,  0, 0, 0,
   1170     //       a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
   1171          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
   1172     //   p   q   r   s   t   u   v   w   x   y   z
   1173          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
   1174     };
   1175 
   1176     int8_t PATTERN_CHAR_BASE = 0x41;
   1177 
   1178     /* loop through the pattern string character by character looking for
   1179      * the first repeated pattern letter, which breaks the interval pattern
   1180      * into 2 parts.
   1181      */
   1182     int32_t i;
   1183     UBool foundRepetition = false;
   1184     for (i = 0; i < intervalPattern.length(); ++i) {
   1185         UChar ch = intervalPattern.charAt(i);
   1186 
   1187         if (ch != prevCh && count > 0) {
   1188             // check the repeativeness of pattern letter
   1189             UBool repeated = patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)];
   1190             if ( repeated == FALSE ) {
   1191                 patternRepeated[prevCh - PATTERN_CHAR_BASE] = TRUE;
   1192             } else {
   1193                 foundRepetition = true;
   1194                 break;
   1195             }
   1196             count = 0;
   1197         }
   1198         if (ch == '\'') {
   1199             // Consecutive single quotes are a single quote literal,
   1200             // either outside of quotes or between quotes
   1201             if ((i+1) < intervalPattern.length() &&
   1202                 intervalPattern.charAt(i+1) == '\'') {
   1203                 ++i;
   1204             } else {
   1205                 inQuote = ! inQuote;
   1206             }
   1207         }
   1208         else if (!inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/)
   1209                     || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) {
   1210             // ch is a date-time pattern character
   1211             prevCh = ch;
   1212             ++count;
   1213         }
   1214     }
   1215     // check last pattern char, distinguish
   1216     // "dd MM" ( no repetition ),
   1217     // "d-d"(last char repeated ), and
   1218     // "d-d MM" ( repetition found )
   1219     if ( count > 0 && foundRepetition == FALSE ) {
   1220         if ( patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)] == FALSE ) {
   1221             count = 0;
   1222         }
   1223     }
   1224     return (i - count);
   1225 }
   1226 
   1227 
   1228 
   1229 UnicodeString&
   1230 DateIntervalFormat::fallbackFormat(Calendar& fromCalendar,
   1231                                    Calendar& toCalendar,
   1232                                    UnicodeString& appendTo,
   1233                                    FieldPosition& pos,
   1234                                    UErrorCode& status) const {
   1235     if ( U_FAILURE(status) ) {
   1236         return appendTo;
   1237     }
   1238     // the fall back
   1239     // no need delete earlierDate and laterDate since they are adopted
   1240     UnicodeString* earlierDate = new UnicodeString();
   1241     *earlierDate = fDateFormat->format(fromCalendar, *earlierDate, pos);
   1242     UnicodeString* laterDate = new UnicodeString();
   1243     *laterDate = fDateFormat->format(toCalendar, *laterDate, pos);
   1244     UnicodeString fallbackPattern;
   1245     fInfo->getFallbackIntervalPattern(fallbackPattern);
   1246     Formattable fmtArray[2];
   1247     fmtArray[0].adoptString(earlierDate);
   1248     fmtArray[1].adoptString(laterDate);
   1249 
   1250     UnicodeString fallback;
   1251     MessageFormat::format(fallbackPattern, fmtArray, 2, fallback, status);
   1252     if ( U_SUCCESS(status) ) {
   1253         appendTo.append(fallback);
   1254     }
   1255     return appendTo;
   1256 }
   1257 
   1258 
   1259 
   1260 
   1261 UBool  U_EXPORT2
   1262 DateIntervalFormat::fieldExistsInSkeleton(UCalendarDateFields field,
   1263                                           const UnicodeString& skeleton)
   1264 {
   1265     const UChar fieldChar = fgCalendarFieldToPatternLetter[field];
   1266     return ( (skeleton.indexOf(fieldChar) == -1)?FALSE:TRUE ) ;
   1267 }
   1268 
   1269 
   1270 
   1271 void  U_EXPORT2
   1272 DateIntervalFormat::adjustFieldWidth(const UnicodeString& inputSkeleton,
   1273                  const UnicodeString& bestMatchSkeleton,
   1274                  const UnicodeString& bestIntervalPattern,
   1275                  int8_t differenceInfo,
   1276                  UnicodeString& adjustedPtn) {
   1277     adjustedPtn = bestIntervalPattern;
   1278     int32_t inputSkeletonFieldWidth[] =
   1279     {
   1280     //       A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
   1281              0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
   1282     //   P   Q   R   S   T   U   V   W   X   Y   Z
   1283          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 0, 0,  0, 0, 0,
   1284     //       a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
   1285          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
   1286     //   p   q   r   s   t   u   v   w   x   y   z
   1287          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
   1288     };
   1289 
   1290     int32_t bestMatchSkeletonFieldWidth[] =
   1291     {
   1292     //       A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
   1293              0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
   1294     //   P   Q   R   S   T   U   V   W   X   Y   Z
   1295          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 0, 0,  0, 0, 0,
   1296     //       a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
   1297          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
   1298     //   p   q   r   s   t   u   v   w   x   y   z
   1299          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
   1300     };
   1301 
   1302     DateIntervalInfo::parseSkeleton(inputSkeleton, inputSkeletonFieldWidth);
   1303     DateIntervalInfo::parseSkeleton(bestMatchSkeleton, bestMatchSkeletonFieldWidth);
   1304     if ( differenceInfo == 2 ) {
   1305         adjustedPtn.findAndReplace("v", "z");
   1306     }
   1307 
   1308     UBool inQuote = false;
   1309     UChar prevCh = 0;
   1310     int32_t count = 0;
   1311 
   1312     const int8_t PATTERN_CHAR_BASE = 0x41;
   1313 
   1314     // loop through the pattern string character by character
   1315     int32_t adjustedPtnLength = adjustedPtn.length();
   1316     int32_t i;
   1317     for (i = 0; i < adjustedPtnLength; ++i) {
   1318         UChar ch = adjustedPtn.charAt(i);
   1319         if (ch != prevCh && count > 0) {
   1320             // check the repeativeness of pattern letter
   1321             UChar skeletonChar = prevCh;
   1322             if ( skeletonChar ==  CAP_L ) {
   1323                 // there is no "L" (always be "M") in skeleton,
   1324                 // but there is "L" in pattern.
   1325                 // for skeleton "M+", the pattern might be "...L..."
   1326                 skeletonChar = CAP_M;
   1327             }
   1328             int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)];
   1329             int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)];
   1330             if ( fieldCount == count && inputFieldCount > fieldCount ) {
   1331                 count = inputFieldCount - fieldCount;
   1332                 int32_t j;
   1333                 for ( j = 0; j < count; ++j ) {
   1334                     adjustedPtn.insert(i, prevCh);
   1335                 }
   1336                 i += count;
   1337                 adjustedPtnLength += count;
   1338             }
   1339             count = 0;
   1340         }
   1341         if (ch == '\'') {
   1342             // Consecutive single quotes are a single quote literal,
   1343             // either outside of quotes or between quotes
   1344             if ((i+1) < adjustedPtn.length() && adjustedPtn.charAt(i+1) == '\'') {
   1345                 ++i;
   1346             } else {
   1347                 inQuote = ! inQuote;
   1348             }
   1349         }
   1350         else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/)
   1351                     || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) {
   1352             // ch is a date-time pattern character
   1353             prevCh = ch;
   1354             ++count;
   1355         }
   1356     }
   1357     if ( count > 0 ) {
   1358         // last item
   1359         // check the repeativeness of pattern letter
   1360         UChar skeletonChar = prevCh;
   1361         if ( skeletonChar == CAP_L ) {
   1362             // there is no "L" (always be "M") in skeleton,
   1363             // but there is "L" in pattern.
   1364             // for skeleton "M+", the pattern might be "...L..."
   1365             skeletonChar = CAP_M;
   1366         }
   1367         int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)];
   1368         int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)];
   1369         if ( fieldCount == count && inputFieldCount > fieldCount ) {
   1370             count = inputFieldCount - fieldCount;
   1371             int32_t j;
   1372             for ( j = 0; j < count; ++j ) {
   1373                 adjustedPtn.append(prevCh);
   1374             }
   1375         }
   1376     }
   1377 }
   1378 
   1379 
   1380 
   1381 void
   1382 DateIntervalFormat::concatSingleDate2TimeInterval(const UChar* format,
   1383                                               int32_t formatLen,
   1384                                               const UnicodeString& datePattern,
   1385                                               UCalendarDateFields field,
   1386                                               UErrorCode& status) {
   1387     // following should not set wrong status
   1388     int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field,
   1389                                                                         status);
   1390     if ( U_FAILURE(status) ) {
   1391         return;
   1392     }
   1393     PatternInfo&  timeItvPtnInfo = fIntervalPatterns[itvPtnIndex];
   1394     if ( !timeItvPtnInfo.firstPart.isEmpty() ) {
   1395         // UnicodeString allocated here is adopted, so no need to delete
   1396         UnicodeString* timeIntervalPattern = new UnicodeString(timeItvPtnInfo.firstPart);
   1397         timeIntervalPattern->append(timeItvPtnInfo.secondPart);
   1398         UnicodeString* dateStr = new UnicodeString(datePattern);
   1399         Formattable fmtArray[2];
   1400         fmtArray[0].adoptString(timeIntervalPattern);
   1401         fmtArray[1].adoptString(dateStr);
   1402         UnicodeString combinedPattern;
   1403         MessageFormat::format(UnicodeString(TRUE, format, formatLen),
   1404                               fmtArray, 2, combinedPattern, status);
   1405         if ( U_FAILURE(status) ) {
   1406             return;
   1407         }
   1408         setIntervalPattern(field, combinedPattern, timeItvPtnInfo.laterDateFirst);
   1409     }
   1410     // else: fall back
   1411     // it should not happen if the interval format defined is valid
   1412 }
   1413 
   1414 
   1415 
   1416 const UChar
   1417 DateIntervalFormat::fgCalendarFieldToPatternLetter[] =
   1418 {
   1419     /*GyM*/ CAP_G, LOW_Y, CAP_M,
   1420     /*wWd*/ LOW_W, CAP_W, LOW_D,
   1421     /*DEF*/ CAP_D, CAP_E, CAP_F,
   1422     /*ahH*/ LOW_A, LOW_H, CAP_H,
   1423     /*m..*/ LOW_M,
   1424 };
   1425 
   1426 
   1427 U_NAMESPACE_END
   1428 
   1429 #endif
   1430