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