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