intl/icu/source/i18n/dtitvfmt.cpp

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

     1 /*******************************************************************************
     2 * Copyright (C) 2008-2013, International Business Machines Corporation and
     3 * others. All Rights Reserved.
     4 *******************************************************************************
     5 *
     6 * File DTITVFMT.CPP 
     7 *
     8 *******************************************************************************
     9 */
    11 #include "utypeinfo.h"  // for 'typeid' to work
    13 #include "unicode/dtitvfmt.h"
    15 #if !UCONFIG_NO_FORMATTING
    17 //TODO: put in compilation
    18 //#define DTITVFMT_DEBUG 1
    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"
    27 #ifdef DTITVFMT_DEBUG 
    28 #include <iostream>
    29 #include "cstring.h"
    30 #endif
    32 #include "gregoimp.h"
    34 U_NAMESPACE_BEGIN
    38 #ifdef DTITVFMT_DEBUG 
    39 #define PRINTMESG(msg) { std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; }
    40 #endif
    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} };
    54 static const char gDateTimePatternsTag[]="DateTimePatterns";
    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};
    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};
    64 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalFormat)
    68 DateIntervalFormat* U_EXPORT2
    69 DateIntervalFormat::createInstance(const UnicodeString& skeleton, 
    70                                    UErrorCode& status) {
    71     return createInstance(skeleton, Locale::getDefault(), status);
    72 }
    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
    91     DateIntervalInfo* dtitvinf = new DateIntervalInfo(locale, status);
    92     return create(locale, dtitvinf, &skeleton, status);
    93 }
    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 }
   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 }
   115 DateIntervalFormat::DateIntervalFormat()
   116 :   fInfo(NULL),
   117     fDateFormat(NULL),
   118     fFromCalendar(NULL),
   119     fToCalendar(NULL),
   120     fDtpng(NULL)
   121 {}
   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 }
   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 }
   176 DateIntervalFormat::~DateIntervalFormat() {
   177     delete fInfo;
   178     delete fDateFormat;
   179     delete fFromCalendar;
   180     delete fToCalendar;
   181     delete fDtpng;
   182 }
   185 Format*
   186 DateIntervalFormat::clone(void) const {
   187     return new DateIntervalFormat(*this);
   188 }
   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);
   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 }
   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     }
   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 }
   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     }
   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 }
   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     }
   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     }
   294     // First, find the largest different calendar field.
   295     UCalendarDateFields field = UCAL_FIELD_COUNT;
   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     }
   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     }
   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];
   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 }
   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 }
   395 const DateIntervalInfo*
   396 DateIntervalFormat::getDateIntervalInfo() const {
   397     return fInfo;
   398 }
   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 }
   413 const DateFormat*
   414 DateIntervalFormat::getDateFormat() const {
   415     return fDateFormat;
   416 }
   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 }
   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 }
   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 }
   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 }
   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     }
   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 }
   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 }
   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     }
   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     }
   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;
   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);
   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
   651     UBool found = setSeparateDateTimePtn(normalizedDateSkeleton, 
   652                                          normalizedTimeSkeleton);
   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         }
   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);
   736         if ( U_FAILURE(status) ) {
   737             delete calData;
   738             return;
   739         }
   741         if ( calData == NULL ) {
   742             status = U_MEMORY_ALLOCATION_ERROR;
   743             return;
   744         }
   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         }
   757         UnicodeString datePattern = fDtpng->getBestPattern(dateSkeleton, status);
   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 }
   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;
   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             normalizedDateSkeleton.append(ch);
   824             dateSkeleton.append(ch);
   825             break;
   826           case LOW_A:
   827             // 'a' is implicitly handled 
   828             timeSkeleton.append(ch);
   829             break;
   830           case LOW_H:
   831             timeSkeleton.append(ch);
   832             ++hCount;
   833             break;
   834           case CAP_H:
   835             timeSkeleton.append(ch);
   836             ++HCount;
   837             break;
   838           case LOW_M:
   839             timeSkeleton.append(ch);
   840             ++mCount;
   841             break;
   842           case LOW_Z:
   843             ++zCount;
   844             timeSkeleton.append(ch);
   845             break;
   846           case LOW_V:
   847             ++vCount;
   848             timeSkeleton.append(ch);
   849             break;
   850           case CAP_V:
   851           case CAP_Z:
   852           case LOW_K:
   853           case CAP_K:
   854           case LOW_J:
   855           case LOW_S:
   856           case CAP_S:
   857           case CAP_A:
   858             timeSkeleton.append(ch);
   859             normalizedTimeSkeleton.append(ch);
   860             break;     
   861         }
   862     }
   864     /* generate normalized form for date*/
   865     if ( yCount != 0 ) {
   866         for (i = 0; i < yCount; ++i) {
   867             normalizedDateSkeleton.append(LOW_Y);
   868         }
   869     }
   870     if ( MCount != 0 ) {
   871         if ( MCount < 3 ) {
   872             normalizedDateSkeleton.append(CAP_M);
   873         } else {
   874             int32_t i;
   875             for ( i = 0; i < MCount && i < MAX_M_COUNT; ++i ) {
   876                  normalizedDateSkeleton.append(CAP_M);
   877             }
   878         }
   879     }
   880     if ( ECount != 0 ) {
   881         if ( ECount <= 3 ) {
   882             normalizedDateSkeleton.append(CAP_E);
   883         } else {
   884             int32_t i;
   885             for ( i = 0; i < ECount && i < MAX_E_COUNT; ++i ) {
   886                  normalizedDateSkeleton.append(CAP_E);
   887             }
   888         }
   889     }
   890     if ( dCount != 0 ) {
   891         normalizedDateSkeleton.append(LOW_D);
   892     }
   894     /* generate normalized form for time */
   895     if ( HCount != 0 ) {
   896         normalizedTimeSkeleton.append(CAP_H);
   897     }
   898     else if ( hCount != 0 ) {
   899         normalizedTimeSkeleton.append(LOW_H);
   900     }
   901     if ( mCount != 0 ) {
   902         normalizedTimeSkeleton.append(LOW_M);
   903     }
   904     if ( zCount != 0 ) {
   905         normalizedTimeSkeleton.append(LOW_Z);
   906     }
   907     if ( vCount != 0 ) {
   908         normalizedTimeSkeleton.append(LOW_V);
   909     }
   910 }
   913 /**
   914  * Generate date or time interval pattern from resource,
   915  * and set them into the interval pattern locale to this formatter.
   916  *
   917  * It needs to handle the following: 
   918  * 1. need to adjust field width.
   919  *    For example, the interval patterns saved in DateIntervalInfo
   920  *    includes "dMMMy", but not "dMMMMy".
   921  *    Need to get interval patterns for dMMMMy from dMMMy.
   922  *    Another example, the interval patterns saved in DateIntervalInfo
   923  *    includes "hmv", but not "hmz".
   924  *    Need to get interval patterns for "hmz' from 'hmv'
   925  *
   926  * 2. there might be no pattern for 'y' differ for skeleton "Md",
   927  *    in order to get interval patterns for 'y' differ,
   928  *    need to look for it from skeleton 'yMd'
   929  *
   930  * @param dateSkeleton   normalized date skeleton
   931  * @param timeSkeleton   normalized time skeleton
   932  * @return               whether the resource is found for the skeleton.
   933  *                       TRUE if interval pattern found for the skeleton,
   934  *                       FALSE otherwise.
   935  * @stable ICU 4.0
   936  */
   937 UBool 
   938 DateIntervalFormat::setSeparateDateTimePtn(
   939                                  const UnicodeString& dateSkeleton,
   940                                  const UnicodeString& timeSkeleton) {
   941     const UnicodeString* skeleton;
   942     // if both date and time skeleton present,
   943     // the final interval pattern might include time interval patterns
   944     // ( when, am_pm, hour, minute differ ),
   945     // but not date interval patterns ( when year, month, day differ ).
   946     // For year/month/day differ, it falls back to fall-back pattern.
   947     if ( timeSkeleton.length() != 0  ) {
   948         skeleton = &timeSkeleton;
   949     } else {
   950         skeleton = &dateSkeleton;
   951     }
   953     /* interval patterns for skeleton "dMMMy" (but not "dMMMMy") 
   954      * are defined in resource,
   955      * interval patterns for skeleton "dMMMMy" are calculated by
   956      * 1. get the best match skeleton for "dMMMMy", which is "dMMMy"
   957      * 2. get the interval patterns for "dMMMy",
   958      * 3. extend "MMM" to "MMMM" in above interval patterns for "dMMMMy" 
   959      * getBestSkeleton() is step 1.
   960      */
   961     // best skeleton, and the difference information
   962     int8_t differenceInfo = 0;
   963     const UnicodeString* bestSkeleton = fInfo->getBestSkeleton(*skeleton, 
   964                                                                differenceInfo);
   965     /* best skeleton could be NULL.
   966        For example: in "ca" resource file,
   967        interval format is defined as following
   968            intervalFormats{
   969                 fallback{"{0} - {1}"}
   970             }
   971        there is no skeletons/interval patterns defined,
   972        and the best skeleton match could be NULL
   973      */
   974     if ( bestSkeleton == NULL ) {
   975         return false; 
   976     } 
   978     // difference:
   979     // 0 means the best matched skeleton is the same as input skeleton
   980     // 1 means the fields are the same, but field width are different
   981     // 2 means the only difference between fields are v/z,
   982     // -1 means there are other fields difference 
   983     if ( differenceInfo == -1 ) { 
   984         // skeleton has different fields, not only  v/z difference
   985         return false;
   986     }
   988     if ( timeSkeleton.length() == 0 ) {
   989         UnicodeString extendedSkeleton;
   990         UnicodeString extendedBestSkeleton;
   991         // only has date skeleton
   992         setIntervalPattern(UCAL_DATE, skeleton, bestSkeleton, differenceInfo,
   993                            &extendedSkeleton, &extendedBestSkeleton);
   995         UBool extended = setIntervalPattern(UCAL_MONTH, skeleton, bestSkeleton, 
   996                                      differenceInfo,
   997                                      &extendedSkeleton, &extendedBestSkeleton);
   999         if ( extended ) {
  1000             bestSkeleton = &extendedBestSkeleton;
  1001             skeleton = &extendedSkeleton;
  1003         setIntervalPattern(UCAL_YEAR, skeleton, bestSkeleton, differenceInfo,
  1004                            &extendedSkeleton, &extendedBestSkeleton);
  1005     } else {
  1006         setIntervalPattern(UCAL_MINUTE, skeleton, bestSkeleton, differenceInfo);
  1007         setIntervalPattern(UCAL_HOUR, skeleton, bestSkeleton, differenceInfo);
  1008         setIntervalPattern(UCAL_AM_PM, skeleton, bestSkeleton, differenceInfo);
  1010     return true;
  1015 void
  1016 DateIntervalFormat::setFallbackPattern(UCalendarDateFields field,
  1017                                        const UnicodeString& skeleton,
  1018                                        UErrorCode& status) {
  1019     if ( U_FAILURE(status) ) {
  1020         return;
  1022     UnicodeString pattern = fDtpng->getBestPattern(skeleton, status);
  1023     if ( U_FAILURE(status) ) {
  1024         return;
  1026     setPatternInfo(field, NULL, &pattern, fInfo->getDefaultOrder());
  1032 void
  1033 DateIntervalFormat::setPatternInfo(UCalendarDateFields field, 
  1034                                    const UnicodeString* firstPart,
  1035                                    const UnicodeString* secondPart, 
  1036                                    UBool laterDateFirst) {
  1037     // for fall back interval patterns,
  1038     // the first part of the pattern is empty,
  1039     // the second part of the pattern is the full-pattern
  1040     // should be used in fall-back.
  1041     UErrorCode status = U_ZERO_ERROR;
  1042     // following should not set any wrong status.
  1043     int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field,
  1044                                                                         status);
  1045     if ( U_FAILURE(status) ) {
  1046         return;
  1048     PatternInfo& ptn = fIntervalPatterns[itvPtnIndex];
  1049     if ( firstPart ) {
  1050         ptn.firstPart = *firstPart;
  1052     if ( secondPart ) {
  1053         ptn.secondPart = *secondPart;
  1055     ptn.laterDateFirst = laterDateFirst;
  1058 void
  1059 DateIntervalFormat::setIntervalPattern(UCalendarDateFields field,
  1060                                        const UnicodeString& intervalPattern) {
  1061     UBool order = fInfo->getDefaultOrder();
  1062     setIntervalPattern(field, intervalPattern, order);
  1066 void
  1067 DateIntervalFormat::setIntervalPattern(UCalendarDateFields field,
  1068                                        const UnicodeString& intervalPattern,
  1069                                        UBool laterDateFirst) {
  1070     const UnicodeString* pattern = &intervalPattern;
  1071     UBool order = laterDateFirst;
  1072     // check for "latestFirst:" or "earliestFirst:" prefix
  1073     int8_t prefixLength = sizeof(gLaterFirstPrefix)/sizeof(gLaterFirstPrefix[0]);
  1074     int8_t earliestFirstLength = sizeof(gEarlierFirstPrefix)/sizeof(gEarlierFirstPrefix[0]);
  1075     UnicodeString realPattern;
  1076     if ( intervalPattern.startsWith(gLaterFirstPrefix, prefixLength) ) {
  1077         order = true;
  1078         intervalPattern.extract(prefixLength, 
  1079                                 intervalPattern.length() - prefixLength,
  1080                                 realPattern);
  1081         pattern = &realPattern;
  1082     } else if ( intervalPattern.startsWith(gEarlierFirstPrefix,
  1083                                            earliestFirstLength) ) {
  1084         order = false;
  1085         intervalPattern.extract(earliestFirstLength,
  1086                                 intervalPattern.length() - earliestFirstLength,
  1087                                 realPattern);
  1088         pattern = &realPattern;
  1091     int32_t splitPoint = splitPatternInto2Part(*pattern);
  1093     UnicodeString firstPart;
  1094     UnicodeString secondPart;
  1095     pattern->extract(0, splitPoint, firstPart);
  1096     if ( splitPoint < pattern->length() ) {
  1097         pattern->extract(splitPoint, pattern->length()-splitPoint, secondPart);
  1099     setPatternInfo(field, &firstPart, &secondPart, order);
  1105 /**
  1106  * Generate interval pattern from existing resource
  1108  * It not only save the interval patterns,
  1109  * but also return the extended skeleton and its best match skeleton.
  1111  * @param field           largest different calendar field
  1112  * @param skeleton        skeleton
  1113  * @param bestSkeleton    the best match skeleton which has interval pattern
  1114  *                        defined in resource
  1115  * @param differenceInfo  the difference between skeleton and best skeleton
  1116  *         0 means the best matched skeleton is the same as input skeleton
  1117  *         1 means the fields are the same, but field width are different
  1118  *         2 means the only difference between fields are v/z,
  1119  *        -1 means there are other fields difference 
  1121  * @param extendedSkeleton      extended skeleton
  1122  * @param extendedBestSkeleton  extended best match skeleton
  1123  * @return                      whether the interval pattern is found 
  1124  *                              through extending skeleton or not.
  1125  *                              TRUE if interval pattern is found by
  1126  *                              extending skeleton, FALSE otherwise.
  1127  * @stable ICU 4.0
  1128  */
  1129 UBool
  1130 DateIntervalFormat::setIntervalPattern(UCalendarDateFields field,
  1131                                        const UnicodeString* skeleton,
  1132                                        const UnicodeString* bestSkeleton,
  1133                                        int8_t differenceInfo,
  1134                                        UnicodeString* extendedSkeleton,
  1135                                        UnicodeString* extendedBestSkeleton) {
  1136     UErrorCode status = U_ZERO_ERROR;
  1137     // following getIntervalPattern() should not generate error status
  1138     UnicodeString pattern;
  1139     fInfo->getIntervalPattern(*bestSkeleton, field, pattern, status);
  1140     if ( pattern.isEmpty() ) {
  1141         // single date
  1142         if ( SimpleDateFormat::isFieldUnitIgnored(*bestSkeleton, field) ) {
  1143             // do nothing, format will handle it
  1144             return false;
  1147         // for 24 hour system, interval patterns in resource file
  1148         // might not include pattern when am_pm differ, 
  1149         // which should be the same as hour differ.
  1150         // add it here for simplicity
  1151         if ( field == UCAL_AM_PM ) {
  1152             fInfo->getIntervalPattern(*bestSkeleton, UCAL_HOUR, pattern,status);
  1153             if ( !pattern.isEmpty() ) {
  1154                 setIntervalPattern(field, pattern);
  1156             return false;
  1158         // else, looking for pattern when 'y' differ for 'dMMMM' skeleton,
  1159         // first, get best match pattern "MMMd",
  1160         // since there is no pattern for 'y' differs for skeleton 'MMMd',
  1161         // need to look for it from skeleton 'yMMMd',
  1162         // if found, adjust field width in interval pattern from
  1163         // "MMM" to "MMMM".
  1164         UChar fieldLetter = fgCalendarFieldToPatternLetter[field];
  1165         if ( extendedSkeleton ) {
  1166             *extendedSkeleton = *skeleton;
  1167             *extendedBestSkeleton = *bestSkeleton;
  1168             extendedSkeleton->insert(0, fieldLetter);
  1169             extendedBestSkeleton->insert(0, fieldLetter);
  1170             // for example, looking for patterns when 'y' differ for
  1171             // skeleton "MMMM". 
  1172             fInfo->getIntervalPattern(*extendedBestSkeleton,field,pattern,status);
  1173             if ( pattern.isEmpty() && differenceInfo == 0 ) {
  1174                 // if there is no skeleton "yMMMM" defined,
  1175                 // look for the best match skeleton, for example: "yMMM" 
  1176                 const UnicodeString* tmpBest = fInfo->getBestSkeleton(
  1177                                         *extendedBestSkeleton, differenceInfo);
  1178                 if ( tmpBest != 0 && differenceInfo != -1 ) {
  1179                     fInfo->getIntervalPattern(*tmpBest, field, pattern, status);
  1180                     bestSkeleton = tmpBest;
  1185     if ( !pattern.isEmpty() ) {
  1186         if ( differenceInfo != 0 ) {
  1187             UnicodeString adjustIntervalPattern;
  1188             adjustFieldWidth(*skeleton, *bestSkeleton, pattern, differenceInfo,
  1189                               adjustIntervalPattern);
  1190             setIntervalPattern(field, adjustIntervalPattern);
  1191         } else {
  1192             setIntervalPattern(field, pattern);
  1194         if ( extendedSkeleton && !extendedSkeleton->isEmpty() ) {
  1195             return TRUE;
  1198     return FALSE;
  1203 int32_t  U_EXPORT2 
  1204 DateIntervalFormat::splitPatternInto2Part(const UnicodeString& intervalPattern) {
  1205     UBool inQuote = false;
  1206     UChar prevCh = 0;
  1207     int32_t count = 0;
  1209     /* repeatedPattern used to record whether a pattern has already seen.
  1210        It is a pattern applies to first calendar if it is first time seen,
  1211        otherwise, it is a pattern applies to the second calendar
  1212      */
  1213     UBool patternRepeated[] = 
  1215     //       A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
  1216              0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  1217     //   P   Q   R   S   T   U   V   W   X   Y   Z
  1218          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 0, 0,  0, 0, 0,
  1219     //       a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
  1220          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  1221     //   p   q   r   s   t   u   v   w   x   y   z
  1222          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
  1223     };
  1225     int8_t PATTERN_CHAR_BASE = 0x41;
  1227     /* loop through the pattern string character by character looking for
  1228      * the first repeated pattern letter, which breaks the interval pattern
  1229      * into 2 parts. 
  1230      */
  1231     int32_t i;
  1232     UBool foundRepetition = false;
  1233     for (i = 0; i < intervalPattern.length(); ++i) {
  1234         UChar ch = intervalPattern.charAt(i);
  1236         if (ch != prevCh && count > 0) {
  1237             // check the repeativeness of pattern letter
  1238             UBool repeated = patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)];
  1239             if ( repeated == FALSE ) {
  1240                 patternRepeated[prevCh - PATTERN_CHAR_BASE] = TRUE;
  1241             } else {
  1242                 foundRepetition = true;
  1243                 break;
  1245             count = 0;
  1247         if (ch == '\'') {
  1248             // Consecutive single quotes are a single quote literal,
  1249             // either outside of quotes or between quotes
  1250             if ((i+1) < intervalPattern.length() && 
  1251                 intervalPattern.charAt(i+1) == '\'') {
  1252                 ++i;
  1253             } else {
  1254                 inQuote = ! inQuote;
  1257         else if (!inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/)
  1258                     || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) {
  1259             // ch is a date-time pattern character 
  1260             prevCh = ch;
  1261             ++count;
  1264     // check last pattern char, distinguish
  1265     // "dd MM" ( no repetition ), 
  1266     // "d-d"(last char repeated ), and 
  1267     // "d-d MM" ( repetition found )
  1268     if ( count > 0 && foundRepetition == FALSE ) {
  1269         if ( patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)] == FALSE ) {
  1270             count = 0;
  1273     return (i - count);
  1278 UnicodeString& 
  1279 DateIntervalFormat::fallbackFormat(Calendar& fromCalendar,
  1280                                    Calendar& toCalendar,
  1281                                    UnicodeString& appendTo,
  1282                                    FieldPosition& pos,
  1283                                    UErrorCode& status) const {
  1284     if ( U_FAILURE(status) ) {
  1285         return appendTo;
  1287     // the fall back
  1288     // no need delete earlierDate and laterDate since they are adopted
  1289     UnicodeString* earlierDate = new UnicodeString();
  1290     *earlierDate = fDateFormat->format(fromCalendar, *earlierDate, pos);
  1291     UnicodeString* laterDate = new UnicodeString();
  1292     *laterDate = fDateFormat->format(toCalendar, *laterDate, pos);
  1293     UnicodeString fallbackPattern;
  1294     fInfo->getFallbackIntervalPattern(fallbackPattern);
  1295     Formattable fmtArray[2];
  1296     fmtArray[0].adoptString(earlierDate);
  1297     fmtArray[1].adoptString(laterDate);
  1299     UnicodeString fallback;
  1300     MessageFormat::format(fallbackPattern, fmtArray, 2, fallback, status);
  1301     if ( U_SUCCESS(status) ) {
  1302         appendTo.append(fallback);
  1304     return appendTo;
  1310 UBool  U_EXPORT2 
  1311 DateIntervalFormat::fieldExistsInSkeleton(UCalendarDateFields field,
  1312                                           const UnicodeString& skeleton)
  1314     const UChar fieldChar = fgCalendarFieldToPatternLetter[field];
  1315     return ( (skeleton.indexOf(fieldChar) == -1)?FALSE:TRUE ) ;
  1320 void  U_EXPORT2 
  1321 DateIntervalFormat::adjustFieldWidth(const UnicodeString& inputSkeleton,
  1322                  const UnicodeString& bestMatchSkeleton,
  1323                  const UnicodeString& bestIntervalPattern,
  1324                  int8_t differenceInfo,
  1325                  UnicodeString& adjustedPtn) {
  1326     adjustedPtn = bestIntervalPattern;
  1327     int32_t inputSkeletonFieldWidth[] = 
  1329     //       A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
  1330              0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  1331     //   P   Q   R   S   T   U   V   W   X   Y   Z
  1332          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 0, 0,  0, 0, 0,
  1333     //       a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
  1334          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  1335     //   p   q   r   s   t   u   v   w   x   y   z
  1336          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
  1337     };
  1339     int32_t bestMatchSkeletonFieldWidth[] = 
  1341     //       A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
  1342              0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  1343     //   P   Q   R   S   T   U   V   W   X   Y   Z
  1344          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 0, 0,  0, 0, 0,
  1345     //       a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
  1346          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  1347     //   p   q   r   s   t   u   v   w   x   y   z
  1348          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
  1349     };
  1351     DateIntervalInfo::parseSkeleton(inputSkeleton, inputSkeletonFieldWidth);
  1352     DateIntervalInfo::parseSkeleton(bestMatchSkeleton, bestMatchSkeletonFieldWidth);
  1353     if ( differenceInfo == 2 ) {
  1354         adjustedPtn.findAndReplace(UnicodeString((UChar)0x76 /* v */),
  1355                                    UnicodeString((UChar)0x7a /* z */));
  1358     UBool inQuote = false;
  1359     UChar prevCh = 0;
  1360     int32_t count = 0;
  1362     const int8_t PATTERN_CHAR_BASE = 0x41;
  1364     // loop through the pattern string character by character 
  1365     int32_t adjustedPtnLength = adjustedPtn.length();
  1366     int32_t i;
  1367     for (i = 0; i < adjustedPtnLength; ++i) {
  1368         UChar ch = adjustedPtn.charAt(i);
  1369         if (ch != prevCh && count > 0) {
  1370             // check the repeativeness of pattern letter
  1371             UChar skeletonChar = prevCh;
  1372             if ( skeletonChar ==  CAP_L ) {
  1373                 // there is no "L" (always be "M") in skeleton, 
  1374                 // but there is "L" in pattern.
  1375                 // for skeleton "M+", the pattern might be "...L..." 
  1376                 skeletonChar = CAP_M;
  1378             int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)];
  1379             int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)];
  1380             if ( fieldCount == count && inputFieldCount > fieldCount ) {
  1381                 count = inputFieldCount - fieldCount;
  1382                 int32_t j;
  1383                 for ( j = 0; j < count; ++j ) {
  1384                     adjustedPtn.insert(i, prevCh);    
  1386                 i += count;
  1387                 adjustedPtnLength += count;
  1389             count = 0;
  1391         if (ch == '\'') {
  1392             // Consecutive single quotes are a single quote literal,
  1393             // either outside of quotes or between quotes
  1394             if ((i+1) < adjustedPtn.length() && adjustedPtn.charAt(i+1) == '\'') {
  1395                 ++i;
  1396             } else {
  1397                 inQuote = ! inQuote;
  1400         else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) 
  1401                     || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) {
  1402             // ch is a date-time pattern character 
  1403             prevCh = ch;
  1404             ++count;
  1407     if ( count > 0 ) {
  1408         // last item
  1409         // check the repeativeness of pattern letter
  1410         UChar skeletonChar = prevCh;
  1411         if ( skeletonChar == CAP_L ) {
  1412             // there is no "L" (always be "M") in skeleton, 
  1413             // but there is "L" in pattern.
  1414             // for skeleton "M+", the pattern might be "...L..." 
  1415             skeletonChar = CAP_M;
  1417         int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)];
  1418         int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)];
  1419         if ( fieldCount == count && inputFieldCount > fieldCount ) {
  1420             count = inputFieldCount - fieldCount;
  1421             int32_t j;
  1422             for ( j = 0; j < count; ++j ) {
  1423                 adjustedPtn.append(prevCh);    
  1431 void 
  1432 DateIntervalFormat::concatSingleDate2TimeInterval(const UChar* format,
  1433                                               int32_t formatLen,
  1434                                               const UnicodeString& datePattern,
  1435                                               UCalendarDateFields field,
  1436                                               UErrorCode& status) {
  1437     // following should not set wrong status
  1438     int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field,
  1439                                                                         status);
  1440     if ( U_FAILURE(status) ) {
  1441         return;
  1443     PatternInfo&  timeItvPtnInfo = fIntervalPatterns[itvPtnIndex];
  1444     if ( !timeItvPtnInfo.firstPart.isEmpty() ) {
  1445         // UnicodeString allocated here is adopted, so no need to delete
  1446         UnicodeString* timeIntervalPattern = new UnicodeString(timeItvPtnInfo.firstPart);
  1447         timeIntervalPattern->append(timeItvPtnInfo.secondPart);
  1448         UnicodeString* dateStr = new UnicodeString(datePattern);
  1449         Formattable fmtArray[2];
  1450         fmtArray[0].adoptString(timeIntervalPattern);
  1451         fmtArray[1].adoptString(dateStr);
  1452         UnicodeString combinedPattern;
  1453         MessageFormat::format(UnicodeString(TRUE, format, formatLen),
  1454                               fmtArray, 2, combinedPattern, status);
  1455         if ( U_FAILURE(status) ) {
  1456             return;
  1458         setIntervalPattern(field, combinedPattern, timeItvPtnInfo.laterDateFirst);
  1460     // else: fall back
  1461     // it should not happen if the interval format defined is valid
  1466 const UChar
  1467 DateIntervalFormat::fgCalendarFieldToPatternLetter[] =
  1469     /*GyM*/ CAP_G, LOW_Y, CAP_M,
  1470     /*wWd*/ LOW_W, CAP_W, LOW_D,
  1471     /*DEF*/ CAP_D, CAP_E, CAP_F,
  1472     /*ahH*/ LOW_A, LOW_H, CAP_H,
  1473     /*m..*/ LOW_M,
  1474 };
  1477 U_NAMESPACE_END
  1479 #endif

mercurial